From 17402f3ba254113f844ab1f81e00d34e7bbf097d Mon Sep 17 00:00:00 2001
From: winterstacks
Date: Wed, 16 Sep 2026 12:45:04 +0200
Subject: [PATCH 1/3] add the arc rail and make the workload pin refreshable
Arc is Circle's L1 and USDC is its gas token, so a wallet that can pay needs
no second asset to settle. The rail is offered only when X402_ARC_PAY_TO is
set, and a startup probe reads the chain id and the token's EIP-712 domain
before anything is quoted: Arc's USDC reports name() "USDC" where Base
reports "USD Coin", and signing the wrong one produces a signature that
recovers to nobody. A probe failure drops Arc alone rather than the process.
A test network now marks itself in the quote. It sits beside mainnet rails
priced identically, and the chain id was the only thing telling them apart.
The upstream enclave redeployed twice in four days, and a commit pinned only
in a published package refuses every call until each client upgrades. The
accepted commits can now come from /v1/confidential/workload; the launcher
image digest and the OS image hash stay compiled in, so a wrong list can
refuse a good workload but never admit a different one.
Also: an unreachable chain answers 402 instead of taking the gateway down,
payment keys are namespaced by chain so the same nonce is not refused on a
second rail, and a spent nonce is reported as payment_already_redeemed
rather than sharing invalid_transaction_state with every revert.
Signed-off-by: winterstacks
---
inference/gateway.mjs | 71 ++++++++++++++++--
inference/gateway.test.mjs | 101 ++++++++++++++++++++++++-
inference/server.mjs | 102 +++++++++++++++++++++++++-
python-sdk/prismnetwork/__init__.py | 2 +-
python-sdk/prismnetwork/_inference.py | 32 +++++++-
python-sdk/pyproject.toml | 2 +-
python-sdk/tests/test_inference.py | 45 ++++++++++--
scripts/check-prism-health.py | 61 +++++++++++++--
sdk/attest.mjs | 2 +-
sdk/attest.test.mjs | 12 ++-
sdk/fixtures/aci-attestation.json | 2 +-
sdk/package.json | 2 +-
x402/exact-evm.mjs | 6 +-
13 files changed, 405 insertions(+), 35 deletions(-)
diff --git a/inference/gateway.mjs b/inference/gateway.mjs
index 34ccdda..23c0398 100644
--- a/inference/gateway.mjs
+++ b/inference/gateway.mjs
@@ -111,7 +111,7 @@ const TX_HASH = /^0x[0-9a-f]{64}$/i;
// pays first, so its key is the transaction hash. The exact scheme authorizes
// first, so its key is the payer and the authorization nonce, which is also
// what the token contract itself refuses to reuse.
-const PAYMENT_KEY = /^(0x[0-9a-f]{64}|0x[0-9a-f]{40}:0x[0-9a-f]{64})$/i;
+const PAYMENT_KEY = /^([a-z0-9:-]+\|)?(0x[0-9a-f]{64}|0x[0-9a-f]{40}:0x[0-9a-f]{64})$/i;
const ROUTES = {
single: {
@@ -285,6 +285,15 @@ export const USDC_BASE_DOMAIN = { name: "USD Coin", version: "2" };
/// on-chain DOMAIN_SEPARATOR. A client that signs against the wrong domain
/// produces a well-formed signature the token rejects.
export const USDG_ROBINHOOD_DOMAIN = { name: "Global Dollar", version: "1" };
+/// USDC is Arc's gas token, exposed both as an 18-decimal native balance and as
+/// a 6-decimal ERC-20 at this predeploy. x402 amounts are ERC-20 units, so the
+/// payload is unchanged; anything reading a native balance for the same money is
+/// off by 10^12.
+export const USDC_ARC = "0x3600000000000000000000000000000000000000";
+/// Read off the testnet predeploy on 2026-09-14, and it is not Base's domain: Arc's
+/// `name()` is "USDC" where Base returns "USD Coin". Signing Base's domain here
+/// produces a well-formed signature that recovers to nobody.
+export const USDC_ARC_DOMAIN = { name: "USDC", version: "2" };
export function loadConsumed(file) {
const set = new Set();
@@ -302,6 +311,9 @@ export function createGateway({
models,
payTo,
basePayTo = null,
+ arcPayTo = null,
+ arcNetwork = "eip155:5042",
+ arcTestnet = false,
exact = null,
originUrl = "https://api.prismnetwork.tech/inference",
// Carried in every 402 so an agent that arrives cold learns the price and how
@@ -661,7 +673,8 @@ export function createGateway({
detail,
state: phase(),
retry_after_seconds: seconds,
- retry: "nothing was charged; send the same payment header again.",
+ retry: "nothing was charged; send the same payment header again, or sign a "
+ + "fresh one if its validBefore has passed while you waited.",
},
};
}
@@ -678,7 +691,8 @@ export function createGateway({
error,
detail,
state: phase(),
- retry: "nothing was charged; retry with the same payment header",
+ retry: "nothing was charged; retry with the same payment header, or sign a "
+ + "fresh one if its validBefore has passed while you waited",
},
};
}
@@ -742,6 +756,36 @@ export function createGateway({
extra: { ...USDC_BASE_DOMAIN, assetTransferMethod: "eip3009" },
});
}
+ if (arcPayTo) {
+ list.push({
+ scheme: "exact",
+ network: arcNetwork,
+ asset: USDC_ARC,
+ payTo: arcPayTo,
+ amount: amount.toString(),
+ resource: `${originUrl}${route.path}`,
+ description:
+ `${route.unit} ${route.where}, paid in USDC on Arc${arcTestnet ? " testnet" : ""}. ` +
+ "Sign an EIP-3009 transferWithAuthorization for the quoted amount and send it as the " +
+ "payment header. You need no gas: the authorization is broadcast for you." +
+ // The other rails in this array settle on mainnets. A quote that lists
+ // a test chain beside them, priced the same and labelled only by its
+ // id, invites a caller to pick the wrong one and spend real money.
+ (arcTestnet ? " TEST NETWORK: this rail settles in play money and buys nothing." : ""),
+ mimeType: "application/json",
+ maxTimeoutSeconds: 60,
+ ...(shape ? { outputSchema: shape } : {}),
+ // v2 strips `description` from an accepts entry and keeps `extra`, so a
+ // warning that only lives in the prose never reaches a v2 client. This
+ // rail sits beside mainnet rails priced identically, and its chain id is
+ // the only other thing telling them apart.
+ extra: {
+ ...USDC_ARC_DOMAIN,
+ assetTransferMethod: "eip3009",
+ ...(arcTestnet ? { testnet: true } : {}),
+ },
+ });
+ }
list.push({
scheme: "exact",
network: "eip155:4663",
@@ -810,9 +854,6 @@ export function createGateway({
if (typeof from !== "string" || typeof nonce !== "string") {
return { ok: false, reason: "invalid_payload" };
}
- const key = `${from}:${nonce}`.toLowerCase();
- if (!PAYMENT_KEY.test(key)) return { ok: false, reason: "invalid_payload" };
-
// The requirement we quoted, not the one the client echoed back: a payer
// who rewrites the amount or the recipient must fail, and comparing their
// copy against itself would always pass.
@@ -823,10 +864,26 @@ export function createGateway({
);
if (!want) return { ok: false, reason: "invalid_network" };
+ // Namespaced by chain: a payer whose client derives nonces deterministically
+ // pays the same nonce on two rails, and an un-namespaced key refuses the
+ // second as a replay. The token's own authorizationState is per chain
+ // already, so this only guards the local set.
+ const key = `${want.network}|${from}:${nonce}`.toLowerCase();
+ if (!PAYMENT_KEY.test(key)) return { ok: false, reason: "invalid_payload" };
+
// Checked before the spent-payment cache is consulted. The authorization
// and its signature are broadcast on chain to settle, so a payer and nonce
// read off a block would otherwise redeem the answer they bought.
- const verdict = await exact.verify(parsed, want);
+ // An unreachable chain is our fault, not the payer's, but 402 is the only
+ // answer that lets them retry or pick another rail. Letting it throw would
+ // take the process down and every warm lease with it.
+ let verdict;
+ try {
+ verdict = await exact.verify(parsed, want);
+ } catch (error) {
+ log(`verify failed on ${want.network}: ${describe(error)}`);
+ return { ok: false, reason: "facilitator_unavailable" };
+ }
if (!verdict.isValid) return { ok: false, reason: verdict.invalidReason };
if (!reservePayment(key)) {
diff --git a/inference/gateway.test.mjs b/inference/gateway.test.mjs
index 0b66205..e882aad 100644
--- a/inference/gateway.test.mjs
+++ b/inference/gateway.test.mjs
@@ -3,7 +3,7 @@ import { test } from "node:test";
import { recoverMessageAddress } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { boundMessage, hashRequest } from "@prismnetwork/x402/codec";
-import { createGateway, DEFAULT_PRICING, priceFor, SERVED_TTL_MS } from "./gateway.mjs";
+import { createGateway, DEFAULT_PRICING, priceFor, SERVED_TTL_MS, USDC_ARC } from "./gateway.mjs";
// What a lease costs and what it produces. The rate is the network's, read off
// /v1/offers and confirmed by settled receipts (900s billed 199,800 micros).
@@ -812,3 +812,102 @@ test("a spent payment answers the request it bought and no other", async () => {
assert.equal(resigned.body.error, "payment_reused");
assert.equal(deps.calls.generations.length, 2, "the second request must not be served from the first");
});
+
+const ARC_PAY_TO = "0xe67a61f8e2aC4057aa22e64306107E7120078447";
+
+test("Arc is offered only when it is configured, and never displaces the other rails", async () => {
+ const without = build(fakeDeps(), { basePayTo: BASE_PAY_TO, exact: fakeExact() });
+ const bare = (await without.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body.accepts;
+ assert.deepEqual(bare.map((a) => a.network), ["eip155:8453", "eip155:4663"]);
+
+ const withArc = build(fakeDeps(), {
+ basePayTo: BASE_PAY_TO,
+ arcPayTo: ARC_PAY_TO,
+ arcNetwork: "eip155:5042",
+ exact: fakeExact(),
+ });
+ const offered = (await withArc.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body.accepts;
+ // Base stays the headline: validators read accepts[0], and leading with a
+ // chain they do not index makes a payable endpoint look unsupported.
+ assert.deepEqual(offered.map((a) => a.network), ["eip155:8453", "eip155:5042", "eip155:4663"]);
+ assert.equal(offered[0].amount, offered[1].amount, "the same work costs the same on either chain");
+});
+
+test("the Arc quote carries Arc's own EIP-712 domain, not Base's", async () => {
+ const gateway = build(fakeDeps(), {
+ arcPayTo: ARC_PAY_TO,
+ arcNetwork: "eip155:5042",
+ exact: fakeExact(),
+ });
+ const arc = (await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body.accepts.find((a) => a.network.startsWith("eip155:5042"));
+ assert.equal(arc.extra.name, "USDC");
+ assert.equal(arc.extra.version, "2");
+ assert.equal(arc.extra.assetTransferMethod, "eip3009");
+ assert.equal(arc.asset, USDC_ARC);
+ assert.equal(arc.payTo, ARC_PAY_TO);
+});
+
+test("the quoted Arc network is the one configured, so a testnet quote cannot read as mainnet", async () => {
+ const gateway = build(fakeDeps(), {
+ arcPayTo: ARC_PAY_TO,
+ arcNetwork: "eip155:5042002",
+ exact: fakeExact(),
+ });
+ const arc = (await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body.accepts.find((a) => a.network.startsWith("eip155:5042"));
+ assert.equal(arc.network, "eip155:5042002");
+});
+
+test("a chain that cannot be reached answers 402 rather than taking the process down", async () => {
+ const unreachable = {
+ handles: () => true,
+ verify: async () => { throw new Error("HTTP request failed: 403"); },
+ settle: async () => { throw new Error("unreachable"); },
+ };
+ const gateway = build(fakeDeps(), {
+ arcPayTo: ARC_PAY_TO,
+ arcNetwork: "eip155:5042",
+ exact: unreachable,
+ });
+ const quote = (await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body;
+ const arc = quote.accepts.find((a) => a.network === "eip155:5042");
+ const header = Buffer.from(JSON.stringify({
+ x402Version: 2,
+ accepted: { scheme: "exact", network: arc.network, asset: arc.asset, payTo: arc.payTo },
+ payload: { authorization: { from: PAYER, to: arc.payTo, value: arc.amount,
+ validAfter: "0", validBefore: String(Math.floor(Date.now() / 1000) + 3600), nonce: `0x${"11".repeat(32)}` },
+ signature: `0x${"22".repeat(65)}` },
+ }), "utf8").toString("base64");
+
+ const answer = await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, header, 2);
+ assert.equal(answer.status, 402, "an unreachable rail is a 402, not a crash");
+ assert.equal(answer.body.error, "facilitator_unavailable");
+});
+
+
+test("a test network says so in the quote, because it sits beside rails that spend real money", async () => {
+ const gateway = build(fakeDeps(), {
+ basePayTo: BASE_PAY_TO,
+ arcPayTo: ARC_PAY_TO,
+ arcNetwork: "eip155:5042002",
+ arcTestnet: true,
+ exact: fakeExact(),
+ });
+ const offered = (await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2)).body.accepts;
+ const arc = offered.find((a) => a.network === "eip155:5042002");
+ // v2 drops `description`, so the marker has to survive in `extra`.
+ assert.equal(arc.extra.testnet, true);
+ // The mainnet rails beside it must stay unmarked, or the warning means nothing.
+ for (const other of offered.filter((a) => a !== arc)) {
+ assert.equal(other.extra?.testnet, undefined, `${other.network} is not a test network`);
+ }
+});
+
+test("the mainnet Arc rail carries no test warning", async () => {
+ const gateway = build(fakeDeps(), {
+ arcPayTo: ARC_PAY_TO, arcNetwork: "eip155:5042", exact: fakeExact(),
+ });
+ const arc = (await gateway.handleInference({ model: "llama3.2:3b", prompt: "hi" }, undefined, 2))
+ .body.accepts.find((a) => a.network === "eip155:5042");
+ assert.equal(arc.extra.testnet, undefined);
+ assert.equal(arc.extra.name, "USDC");
+});
diff --git a/inference/server.mjs b/inference/server.mjs
index f2b2fb4..076f14d 100644
--- a/inference/server.mjs
+++ b/inference/server.mjs
@@ -11,16 +11,18 @@
import { createServer } from "node:http";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
-import { createPublicClient, getAddress, http, recoverMessageAddress } from "viem";
+import { createPublicClient, fallback, getAddress, http, recoverMessageAddress } from "viem";
import { DEFAULT_IMAGE, PrismAgent, robinhoodChain, USDG } from "@prismnetwork/agent-sdk";
import { createExactEvm } from "@prismnetwork/x402/exact-evm";
import { createCdpFacilitator, routeByNetwork } from "@prismnetwork/x402/cdp-facilitator";
import { bazaar, boundMessage, detect, hashRequest } from "@prismnetwork/x402/codec";
import { authorized, listener } from "@prismnetwork/x402/listen";
-import { base as baseChain } from "viem/chains";
+import { arc as arcChain, arcTestnet as arcTestnetChain, base as baseChain } from "viem/chains";
import {
createGateway,
MAX_CONFIDENTIAL_BODY_BYTES,
+ USDC_ARC,
+ USDC_ARC_DOMAIN,
USDC_BASE,
USDC_BASE_DOMAIN,
USDG_ROBINHOOD_DOMAIN,
@@ -113,7 +115,25 @@ try {
// read the receipt back.
baseRpcUrl: (process.env.X402_BASE_RPC_URL ?? "https://base.drpc.org,https://1rpc.io/base")
.split(",").map((u) => u.trim()).filter(Boolean),
+ arcPayTo: process.env.X402_ARC_PAY_TO ? getAddress(process.env.X402_ARC_PAY_TO) : null,
+ arcChain: /^testnet$/i.test((process.env.PRISM_ARC_CHAIN ?? "").trim()) ? arcTestnetChain : arcChain,
+ arcRpcUrl: (process.env.X402_ARC_RPC_URL ?? "").split(",").map((u) => u.trim()).filter(Boolean),
};
+ // Arc mainnet publishes no publicly resolvable RPC, so viem ships that chain
+ // without one. Guessing a hostname here would advertise a rail that cannot
+ // settle, which costs a payer a signature and a wait for nothing.
+ if (config.arcPayTo && !config.arcRpcUrl.length) {
+ if (!config.arcChain.rpcUrls?.default?.http?.length) {
+ throw new Error(
+ `X402_ARC_PAY_TO is set for chain ${config.arcChain.id}, which publishes no default RPC. ` +
+ "Set X402_ARC_RPC_URL, or leave X402_ARC_PAY_TO unset to stop offering Arc.",
+ );
+ }
+ config.arcRpcUrl = [...config.arcChain.rpcUrls.default.http];
+ }
+ if (config.arcPayTo && !process.env.PRISM_X402_COLLECTOR_KEY) {
+ throw new Error("X402_ARC_PAY_TO is set but PRISM_X402_COLLECTOR_KEY is not, so nothing can broadcast an authorization");
+ }
if (config.basePayTo && !process.env.PRISM_X402_COLLECTOR_KEY) {
throw new Error("X402_BASE_PAY_TO is set but PRISM_X402_COLLECTOR_KEY is not, so nothing can broadcast an authorization");
}
@@ -195,6 +215,22 @@ const fetchOllama = (slot, path, init) => fetch(`http://127.0.0.1:${config.tunne
// Robinhood Chain is always verifiable: the agent key that leases GPUs also
// holds the gas to broadcast an authorization there. Base is offered only when
// there is somewhere to collect it.
+const TOKEN_DOMAIN_ABI = [
+ { name: "name", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
+ { name: "version", type: "function", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
+];
+
+// Checked before the network table is built, so a rail that cannot settle is
+// never quoted. Arc alone is dropped: Base and Robinhood do not care that
+// Circle's chain is unreachable, and taking them down with it would turn a
+// misconfigured extra into an outage.
+try {
+ await confirmArcRail();
+} catch (error) {
+ console.error(`arc not offered: ${error.message}`);
+ config.arcPayTo = null;
+}
+
const exactNetworks = {
"eip155:4663": {
chain: robinhoodChain,
@@ -202,6 +238,18 @@ const exactNetworks = {
privateKey: process.env.PRISM_AGENT_KEY,
assets: { [USDG]: USDG_ROBINHOOD_DOMAIN },
},
+ // Gas and takings are the same asset here, so the collector only refills
+ // itself when it is also arcPayTo.
+ ...(config.arcPayTo
+ ? {
+ [`eip155:${config.arcChain.id}`]: {
+ chain: config.arcChain,
+ rpcUrl: config.arcRpcUrl,
+ privateKey: process.env.PRISM_X402_COLLECTOR_KEY,
+ assets: { [USDC_ARC]: USDC_ARC_DOMAIN },
+ },
+ }
+ : {}),
...(config.basePayTo
? {
"eip155:8453": {
@@ -222,6 +270,40 @@ const exactNetworks = {
const localExact = createExactEvm(exactNetworks);
+/// Confirm the Arc rail points where it claims before a payer signs anything.
+///
+/// Two things can be quietly wrong. PRISM_ARC_CHAIN picks the chain while
+/// X402_ARC_RPC_URL picks the endpoint, and nothing ties them together, so a
+/// leftover testnet URL under a mainnet chain id quotes a rail that can never
+/// settle. And the EIP-712 domain is a constant read off testnet; if mainnet's
+/// USDC reports anything else, every signature verifies against nobody.
+
+async function confirmArcRail() {
+ if (!config.arcPayTo) return;
+ const client = createPublicClient({
+ chain: config.arcChain,
+ transport: fallback(config.arcRpcUrl.map((url) => http(url))),
+ });
+ const [id, name, version] = await Promise.all([
+ client.getChainId(),
+ client.readContract({ address: USDC_ARC, abi: TOKEN_DOMAIN_ABI, functionName: "name" }),
+ client.readContract({ address: USDC_ARC, abi: TOKEN_DOMAIN_ABI, functionName: "version" }),
+ ]);
+ if (id !== config.arcChain.id) {
+ throw new Error(
+ `X402_ARC_RPC_URL answers chain ${id} but Arc is configured as ${config.arcChain.id}. ` +
+ "Set PRISM_ARC_CHAIN=testnet for the test network, or point the RPC at mainnet.",
+ );
+ }
+ if (name !== USDC_ARC_DOMAIN.name || version !== USDC_ARC_DOMAIN.version) {
+ throw new Error(
+ `Arc USDC at ${USDC_ARC} reports domain ${JSON.stringify({ name, version })}, ` +
+ `not ${JSON.stringify(USDC_ARC_DOMAIN)}. Every payment would be refused, so Arc is not offered.`,
+ );
+ }
+}
+
+
// Base settles at Coinbase when a key is configured, because the Bazaar only
// indexes endpoints its own facilitator has settled for. Everything else, and
// Base itself when no key is set, stays on ours.
@@ -352,6 +434,9 @@ const gateway = createGateway({
models: config.models,
payTo: config.payTo,
basePayTo: config.basePayTo,
+ arcPayTo: config.arcPayTo,
+ arcNetwork: `eip155:${config.arcChain.id}`,
+ arcTestnet: Boolean(config.arcChain.testnet),
exact,
schemas: inferenceSchemas,
batchSchemas,
@@ -404,6 +489,17 @@ const server = createServer(async (req, res) => {
// The confidential tier as an aggregator's provider monitor reads a
// catalogue. Free, and assembled from live pricing and the live spend cap so
// it says the same thing the 402 does.
+ // The commit the enclave runs moves whenever the upstream redeploys, twice in
+ // four days so far, and a pin that only lives in a published package refuses
+ // every call until each client upgrades. A client may read the accepted
+ // commits here instead; none does yet, so this is the server half.
+ if (req.method === "GET" && url.pathname === "/v1/confidential/workload") {
+ return json(res, 200, {
+ repo_url: "https://github.com/Dstack-TEE/private-ai-gateway.git",
+ repo_commits: (process.env.PRISM_CONFIDENTIAL_COMMITS ?? "")
+ .split(",").map((c) => c.trim().toLowerCase()).filter((c) => /^[0-9a-f]{40}$/.test(c)),
+ }, { "cache-control": "public, max-age=60" });
+ }
if (req.method === "GET" && url.pathname === "/v1/provider/models") {
const catalogue = providerModels({
confidential: gateway.confidential(),
@@ -435,7 +531,7 @@ const server = createServer(async (req, res) => {
x402Version: 1,
name: "Prism Network managed inference",
description:
- "Pay-per-generation LLM inference on rented GPUs. Pay in USDC on Base or USDG on Robinhood " +
+ "Pay-per-generation LLM inference on rented GPUs. Pay in USDC on Base or Arc, or USDG on Robinhood " +
"Chain; an unpaid request answers 402 with the exact price on each. The serving lease " +
"settles onchain with a public receipt." +
(confidential
diff --git a/python-sdk/prismnetwork/__init__.py b/python-sdk/prismnetwork/__init__.py
index 3c5dcab..2bcb224 100644
--- a/python-sdk/prismnetwork/__init__.py
+++ b/python-sdk/prismnetwork/__init__.py
@@ -92,4 +92,4 @@
"keyset_digest",
"report_data",
]
-__version__ = "0.4.0"
+__version__ = "0.4.2"
diff --git a/python-sdk/prismnetwork/_inference.py b/python-sdk/prismnetwork/_inference.py
index 98799e4..632f375 100644
--- a/python-sdk/prismnetwork/_inference.py
+++ b/python-sdk/prismnetwork/_inference.py
@@ -82,7 +82,7 @@
"c083ff9e6a5ddf10f6c9e9bb1f74cc618deebecfea5208b563c574399db4637c",
"repo_url": "https://github.com/Dstack-TEE/private-ai-gateway.git",
"os_image_hash": "bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033",
- "repo_commit": "b6b5c1b82f6fc59490db5a5255bf4493805e66c6",
+ "repo_commit": "3e56bd30dd459d0df90afeb6d63eca7a919bc22f",
}
_ERC20_TRANSFER = [
@@ -414,6 +414,28 @@ class Gate:
skipped: bool = False
+def _accepted_commits(expected) -> set:
+ """Every commit this check will accept.
+
+ The pin exists because a TDX quote alone says nothing about which code was
+ measured. The commit inside it moves whenever the upstream redeploys, which
+ is not our schedule and has happened twice in four days, and a pin that
+ lives only in a published package leaves every installed client refusing to
+ serve until it upgrades.
+
+ So the commit, and only the commit, can also come from ``repo_commits`` in
+ the expected workload, which a caller may refresh from
+ ``/v1/confidential/workload``. The launcher image digest and the OS image
+ hash stay where they are, compiled in. Those identify the binary, so the
+ worst a wrong list can do here is refuse a workload that was fine.
+ """
+ commits = {expected.get("repo_commit")} if expected.get("repo_commit") else set()
+ extra = expected.get("repo_commits")
+ if isinstance(extra, (list, tuple, set)):
+ commits |= {c for c in extra if isinstance(c, str)}
+ return commits
+
+
def _measured_workload(app_compose) -> dict:
"""What the measured compose says it runs. Every value here is inside the
bytes that hash to the measured compose-hash, so none of it is the report's
@@ -459,9 +481,11 @@ def gate_workload_identity(app_compose, os_image_hash, provenance, expected) ->
f"this SDK pins {expected.get('repo_url')}")
if measured["repo_commit"] is None:
problems.append("the measured compose pins no single source commit")
- elif expected.get("repo_commit") and measured["repo_commit"] != str(expected["repo_commit"]).lower():
- problems.append(f"the measured commit is {measured['repo_commit']}, "
- f"this SDK pins {expected['repo_commit']}")
+ else:
+ accepted = {str(c).lower() for c in _accepted_commits(expected) if c}
+ if accepted and measured["repo_commit"] not in accepted:
+ problems.append(f"the measured commit is {measured['repo_commit']}, "
+ f"this SDK accepts {', '.join(sorted(accepted))}")
# A declaration that is not an object declares nothing to contradict the
# measured compose with, and the measured compose is the authority anyway.
diff --git a/python-sdk/pyproject.toml b/python-sdk/pyproject.toml
index 27b6722..6e6dcac 100644
--- a/python-sdk/pyproject.toml
+++ b/python-sdk/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "prismnetwork"
-version = "0.4.0"
+version = "0.4.2"
description = "Headless GPU leasing and metered confidential inference on Prism Network for autonomous agents. Wallet-signature auth, on-chain USDG payment, SSH."
readme = "README.md"
requires-python = ">=3.10"
diff --git a/python-sdk/tests/test_inference.py b/python-sdk/tests/test_inference.py
index 0dcdfea..dac6f34 100644
--- a/python-sdk/tests/test_inference.py
+++ b/python-sdk/tests/test_inference.py
@@ -42,6 +42,7 @@
seal_field,
)
from prismnetwork._inference import (
+ _accepted_commits,
DSTACK_RUNTIME_EVENT,
EXPECTED_WORKLOAD,
ConfidentialError,
@@ -932,17 +933,20 @@ class CapturedReportTest(unittest.TestCase):
"""One live report from the public attestation endpoint, as the offline
vector for the replay and binding checks.
- Its ``app_compose`` travels base64 in the fixture, because the measured
- compose is a shell script whose paths a secret scanner reads as leaked local
- ones, and is restored here byte for byte: the whole point of the check is
- that those bytes hash to the measurement.
+ The endpoint returned ``app_compose`` base64-encoded until 2026-09, because
+ the measured compose is a shell script whose paths a secret scanner reads as
+ leaked local ones. Captures from either era are restored byte for byte here:
+ the whole point of the check is that those bytes hash to the measurement.
"""
@classmethod
def setUpClass(cls):
capture = json.loads(FIXTURE.read_text())
evidence = capture["report"]["attestation"]["evidence"]
- evidence["app_compose"] = base64.b64decode(evidence["app_compose_b64"]).decode("utf-8")
+ # Captures taken before 2026-09 carry the compose base64-encoded; the
+ # endpoint returns it as text now.
+ if "app_compose" not in evidence:
+ evidence["app_compose"] = base64.b64decode(evidence["app_compose_b64"]).decode("utf-8")
cls.report = capture["report"]
cls.nonce = capture["nonce"]
# Before the key set's not_after, which is what the capture is a
@@ -1728,5 +1732,36 @@ def test_a_failure_after_funding_that_is_not_ours_is_still_a_funded_lease(self):
self.assertEqual(caught.exception.broadcast, TX)
+class RefreshableCommitTest(unittest.TestCase):
+ """The commit moves on the upstream's schedule, so it can be refreshed
+ without a release. Nothing else in the pin can."""
+
+ def test_a_refreshed_commit_is_accepted_alongside_the_baked_in_one(self):
+ expected = dict(EXPECTED_WORKLOAD)
+ expected["repo_commits"] = ["a" * 40]
+ accepted = _accepted_commits(expected)
+ self.assertIn(EXPECTED_WORKLOAD["repo_commit"], accepted)
+ self.assertIn("a" * 40, accepted)
+
+ def test_the_baked_in_commit_survives_an_empty_or_junk_refresh(self):
+ for junk in (None, [], "not-a-list", 7, {}):
+ expected = dict(EXPECTED_WORKLOAD, repo_commits=junk)
+ self.assertEqual(
+ _accepted_commits(expected),
+ {EXPECTED_WORKLOAD["repo_commit"]},
+ f"a {type(junk).__name__} refresh must not widen or empty the pin",
+ )
+
+ def test_the_image_and_os_hash_are_not_refreshable(self):
+ expected = dict(EXPECTED_WORKLOAD)
+ expected["repo_commits"] = ["b" * 40]
+ # Only the commit is drawn from the refreshable set; a served list has no
+ # way to name a different launcher image or OS image.
+ self.assertNotIn(expected["launcher_image"], _accepted_commits(expected))
+ self.assertNotIn(expected["os_image_hash"], _accepted_commits(expected))
+
+
if __name__ == "__main__":
unittest.main()
+
+
diff --git a/scripts/check-prism-health.py b/scripts/check-prism-health.py
index 8b9404d..79e2561 100755
--- a/scripts/check-prism-health.py
+++ b/scripts/check-prism-health.py
@@ -26,6 +26,9 @@
import urllib.error
import urllib.request
+# Kept in step with EXPECTED_WORKLOAD in sdk/attest.mjs and _inference.py.
+PINNED_WORKLOAD_COMMIT = "3e56bd30dd459d0df90afeb6d63eca7a919bc22f"
+
DEFAULT_STATE = "/var/lib/prism/health-state.json"
REPEAT_SECONDS = 6 * 60 * 60
# The RPC sits behind a CDN that answers urllib's default agent with a 403.
@@ -310,14 +313,51 @@ def check_canary(alarms, path, stale_seconds):
)
+def check_workload_pin(alarms, attestation_url, pinned_commit):
+ """The SDK pins the exact commit the enclave must be running, and refuses
+ every end-to-end encrypted call when the measurement disagrees. Upstream
+ redeploys on their own schedule, so the pin goes stale without anything on
+ our side changing, and the paid upstream probe above will not notice:
+ it talks to the provider directly and never exercises the sealed path our
+ customers use. Reading the live provenance costs nothing and catches the
+ drift the moment it happens."""
+ if not pinned_commit:
+ alarms.append(Alarm("workload_pin_config", "no pinned commit to compare against"))
+ return
+ report = json.loads(get(attestation_url, timeout=30))
+ measured = (((report.get("attestation") or {}).get("source_provenance") or {})
+ .get("repo_commit"))
+ if not measured:
+ alarms.append(Alarm("workload_pin", "the attestation published no repo_commit"))
+ return
+ if measured != pinned_commit:
+ alarms.append(
+ Alarm(
+ "workload_pin",
+ f"the enclave now measures {measured}, the SDK pins {pinned_commit}; "
+ "every end-to-end encrypted call fails closed until the accepted "
+ "commits are updated",
+ )
+ )
+
+
def check_reconciliation(alarms, url):
"""The monitor already reconciles leases against the escrow. Read its
answers rather than asking the same questions a second way."""
- try:
- body = get(url, timeout=10)
- except (urllib.error.URLError, OSError) as error:
- alarms.append(Alarm("reconcile", f"reconciliation monitor is unreachable: {error}"))
- return
+ # A local scrape that sometimes runs past its budget under load. One slow
+ # read is not an outage, and paging on it produced an unreachable/recovered
+ # pair every few minutes carrying no news about the leases it watches.
+ for attempt in range(2):
+ try:
+ body = get(url, timeout=20)
+ break
+ except (urllib.error.URLError, OSError) as error:
+ if attempt:
+ alarms.append(
+ Alarm("reconcile", f"reconciliation monitor is unreachable on two tries: {error}")
+ )
+ return
+ time.sleep(3)
readings = {}
for line in body.splitlines():
@@ -431,6 +471,17 @@ def main():
os.environ.get("PRISM_CONFIDENTIAL_PROBE_MODEL", "openai/gpt-oss-20b"),
),
),
+ (
+ "workload_pin",
+ lambda: check_workload_pin(
+ alarms,
+ os.environ.get(
+ "PRISM_CONFIDENTIAL_ATTESTATION_URL",
+ "https://tee.redpill.ai/v1/aci/attestation",
+ ),
+ os.environ.get("PRISM_CONFIDENTIAL_PINNED_COMMIT", PINNED_WORKLOAD_COMMIT),
+ ),
+ ),
("reconcile", lambda: check_reconciliation(alarms, metrics_url)),
("canary", lambda: check_canary(alarms, canary_path, canary_stale)),
)
diff --git a/sdk/attest.mjs b/sdk/attest.mjs
index e6a5f9b..9b35141 100644
--- a/sdk/attest.mjs
+++ b/sdk/attest.mjs
@@ -56,7 +56,7 @@ export const EXPECTED_WORKLOAD = {
"ghcr.io/redpill-ai/private-ai-launcher@sha256:c083ff9e6a5ddf10f6c9e9bb1f74cc618deebecfea5208b563c574399db4637c",
repoUrl: "https://github.com/Dstack-TEE/private-ai-gateway.git",
osImageHash: "bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033",
- repoCommit: "b6b5c1b82f6fc59490db5a5255bf4493805e66c6",
+ repoCommit: "3e56bd30dd459d0df90afeb6d63eca7a919bc22f",
};
const NRAS_ATTEST_URL = "https://nras.attestation.nvidia.com/v3/attest/gpu";
diff --git a/sdk/attest.test.mjs b/sdk/attest.test.mjs
index ed187db..f50446c 100644
--- a/sdk/attest.test.mjs
+++ b/sdk/attest.test.mjs
@@ -60,10 +60,14 @@ const specReceipt = { ...JSON.parse(RECEIPT_JCS), signature: RECEIPT_SIGNATURE }
// because the whole point of the check is that those bytes hash to the
// measurement.
const capture = JSON.parse(readFileSync(new URL("./fixtures/aci-attestation.json", import.meta.url), "utf8"));
-capture.report.attestation.evidence.app_compose = Buffer.from(
- capture.report.attestation.evidence.app_compose_b64,
- "base64",
-).toString("utf8");
+// Captures taken before 2026-09 carry the compose base64-encoded; the endpoint
+// returns it as text now.
+if (capture.report.attestation.evidence.app_compose === undefined) {
+ capture.report.attestation.evidence.app_compose = Buffer.from(
+ capture.report.attestation.evidence.app_compose_b64,
+ "base64",
+ ).toString("utf8");
+}
const binding = JSON.parse(readFileSync(new URL("./fixtures/gpu-evidence-binding.json", import.meta.url), "utf8"));
test("canonicalization is member order independent and reproduces the vector digest", async () => {
diff --git a/sdk/fixtures/aci-attestation.json b/sdk/fixtures/aci-attestation.json
index e991d85..86f3326 100644
--- a/sdk/fixtures/aci-attestation.json
+++ b/sdk/fixtures/aci-attestation.json
@@ -1 +1 @@
-{"captured_at":"2026-08-25","endpoint":"https://tee.redpill.ai/v1/aci/attestation","nonce":"ba657b23327cfce615f28e54565d7b2f2717ab6e0b93e1625075dead38ae3d24","report":{"api_version":"aci/1","workload_keyset_digest":"sha256:fa47ae4c27d5df35d063dd6e0cbd258b8cff1a83b3b974b66ca7cb9fea2d1ec9","attestation":{"tee_type":"tdx","workload_keyset":{"subject":null,"not_after":1790174801,"receipt_signing_keys":[{"key_id":"dstack-kms-receipt-ed25519-v1","algo":"ed25519","public_key":"110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a"}],"e2ee_public_keys":[{"key_id":"dstack-kms-e2ee-v1","algo":"secp256k1-aes-256-gcm-hkdf-sha256","public_key":"04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d"},{"key_id":"dstack-kms-e2ee-x25519-v1","algo":"x25519-aes-256-gcm-hkdf-sha256","public_key":"408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537"}],"tls_public_keys":[{"spki_sha256":"22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b","domain":"tee.redpill.ai"},{"spki_sha256":"8bafb8e6ddc55e3e662b6385a2820494fc6e90c64688136860b1851a81b7ee06","domain":"inference.phala.com"},{"spki_sha256":"c68585e7dc00a55d3082a9c860cb916e1788c5daec51505e413d27ab94f7d4c7","domain":"api.redpill.ai"}]},"report_data":"2de4c658f762db2cb8a103b2dad429597e409de8bdd6a5934ba829dcf0cf368b","source_provenance":{"repo_url":"https://github.com/Dstack-TEE/private-ai-gateway.git","repo_commit":"b6b5c1b82f6fc59490db5a5255bf4493805e66c6","image_digest":null,"image_provenance":null},"evidence":{"quote":"040002008100000000000000939a7233f79c4ca9940a0db3957f06074d79685452a1c5725d6ff7f9d09c91b4000000000b0105000000000000000000000000007bf063280e94fb051f5dd7b1fc59ce9aac42bb961df8d44b709c9b0ff87a7b4df648657ba6d1189589feab1d5a3c9a9d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000702000000000000f06dfda6dce1cf904d4e2bab1dc370634cf95cefa2ceb2de2eee127c9382698090d7a4a13e14c536ec6c9c3c8fa8707701ce26ab560d466475b6778456f406dd73da55a3f94dbd1cffc141db63527d3bd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6118f0eeb30e9d9178d2b9106dddd002d979b6fa79bdec415051afae2021384c29a32d2f6454fa369617598378ffb5e07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878cdf67e467e60edc1737bcf8e682d48131bfb427f523226aa7f197a7608e9b3784783fa759ef5b28191fa12f9ddb36b858f23282cbe6b24708467cc26eca437111e3bed80a2f35bbba448ecee37d3de70407232a8534b9df5635a5646cacd9bbcb2de4c658f762db2cb8a103b2dad429597e409de8bdd6a5934ba829dcf0cf368b0000000000000000000000000000000000000000000000000000000000000000cc100000693becb9e6526657ab40a384eee3b04101f51be3edd3d24453b7891bcdbc22a25bbefb8a7f7db0eb3c45b34a924219e7dc0d5780f3aa68c6efb18c13ee3268c2f0bfa28939a048d1d67a664ae6595ec4514829a10a01f7fc10230dd75c44697bf80c2e1e89a20ca66c1f76795d1cf5553a394c311bcc4601b9231b111d70d6b506004610000005050a0a05ff00020000000000000000000000000000000000000000000000000000000000000000000000000000000015000000000000000700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c1dc25ab6fd4ae8afb815c1c46f6d3f9bbe7ab053e699d259bf9136751037b20000000000000000000000000000000000000000000000000000000000000000a233764b3a84792c1aa7f165b48fd0eb5e05077a8672b35ad8242d9c544f0d638082a3002dc29b09308131db2a205e5d0782a72a03278473accd29f17c0806442000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538544343424a656741774942416749555359617753595541735374766a483236625541434d506e6f6a797777436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774d7a49784d4463304e5455795768634e4d7a4d774d7a49784d4463304e5455790a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d34394177454841304941424747690a41664533764c54435150696e5470685047695854516c645948654f6f657257516d4b66745141787939446c63353469446b4b537852786e7a38474f7a466f65690a6b584e532b44374575464864666b476a754e796a67674d4e4d4949444354416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464c6d495a7555304e42506c79336e757041556f41396f590a73625a514d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f67594a4b6f5a496876684e415130420a424949434b7a4343416963774867594b4b6f5a496876684e415130424151515162536e396a546e656c37306e456e776e523951666554434341575147436971470a534962345451454e41514977676746554d42414743797147534962345451454e41514942416745464d42414743797147534962345451454e41514943416745460a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745464d42454743797147534962345451454e41514947416749412f7a415142677371686b69472b453042445145434277494241444151426773710a686b69472b4530424451454343414942416a415142677371686b69472b45304244514543435149424144415142677371686b69472b45304244514543436749420a4144415142677371686b69472b45304244514543437749424144415142677371686b69472b45304244514543444149424144415142677371686b69472b4530420a44514543445149424144415142677371686b69472b45304244514543446749424144415142677371686b69472b453042445145434477494241444151426773710a686b69472b45304244514543454149424144415142677371686b69472b45304244514543455149424454416642677371686b69472b45304244514543456751510a425155434167582f4141494141414141414141414144415142676f71686b69472b45304244514544424149414144415542676f71686b69472b453042445145450a424159676f473841414141774477594b4b6f5a496876684e4151304242516f424154416542676f71686b69472b453042445145474242414e78727a30753075620a48784e354e3964353631576d4d45514743697147534962345451454e415163774e6a415142677371686b69472b45304244514548415145422f7a4151426773710a686b69472b45304244514548416745422f7a415142677371686b69472b45304244514548417745422f7a414b42676771686b6a4f5051514441674e49414442460a4169454131305850553751764338564a743949654f724a366c4430744b58462b54746a4e422f6f7a62635235517477434944576e57773946484e7351745359420a70394c5463792f5670376f517367417076763332732f4236576b73760a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","quote_report_data":"2de4c658f762db2cb8a103b2dad429597e409de8bdd6a5934ba829dcf0cf368b0000000000000000000000000000000000000000000000000000000000000000","event_log":"[{\"imr\":0,\"event_type\":2147483659,\"digest\":\"5fe4f9ee465a361d077a6892f6bc4d19a82b272fea2e3c2c1ef9c33b4d9050115fca0c6dc7a7f68f91c623de38c48226\",\"event\":\"\",\"event_payload\":\"095464785461626c65000100000000000000af96bb93f2b9b84e9462e0ba745642360090800000000000\"},{\"imr\":0,\"event_type\":2147483658,\"digest\":\"344bc51c980ba621aaa00da3ed7436f7d6e549197dfe699515dfa2c6583d95e6412af21c097d473155875ffd561d6790\",\"event\":\"\",\"event_payload\":\"2946762858585858585858582d585858582d585858582d585858582d58585858585858585858585829000000c0ff000000000040080000000000\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"9dc3a1f80bcec915391dcda5ffbb15e7419f77eab462bbf72b42166fb70d50325e37b36f93537a863769bcf9bedae6fb\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0a00000000000000000000000000000053006500630075007200650042006f006f007400\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"6f2e3cbc14f9def86980f5f66fd85e99d63e69a73014ed8a5633ce56eca5b64b692108c56110e22acadcef58c3250f1b\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0200000000000000000000000000000050004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"d607c0efb41c0d757d69bca0615c3a9ac0b1db06c557d992e906c6b7dee40e0e031640c7bfd7bcd35844ef9edeadc6f9\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c030000000000000000000000000000004b0045004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"08a74f8963b337acb6c93682f934496373679dd26af1089cb4eaf0c30cf260a12e814856385ab8843e56a9acea19e127\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f0200000000000000000000000000000064006200\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"18cc6e01f0c6ea99aa23f8a280423e94ad81d96d0aeb5180504fc0f7a40cb3619dd39bd6a95ec1680a86ed6ab0f9828d\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f03000000000000000000000000000000640062007800\"},{\"imr\":0,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":0,\"event_type\":10,\"digest\":\"2058afc033c14a2558b6b3ce664343eeb2b7941427251330e66ff3f12eb5c5706a9c651eee49d656c38ba3090b29ff43\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"2012337df782407ca9495fe820c6744e398a60731d13a2d11d3cdec1c9541e0946b928b929ff28715cc40bcfd71d92b9\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"98731e9fce9603662dba3c4f225a117489960615cb2d893e913c903daeaf09528096875ea94610fdb7eba69a5c20e9e3\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":1,\"event_type\":2147483651,\"digest\":\"90e278aa4d688f4f05bf76f1786fea174abcea0d6b44e81cea5b3327f3f211e5c465a9c34b513283bed2512bca6d4a19\",\"event\":\"\",\"event_payload\":\"1860fd7a0000000000c485000000000000000000000000002a000000000000000403140072f728144ab61e44b8c39ebdd7f893c7040412006b00650072006e0065006c0000007fff0400\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0900000000000000020000000000000042006f006f0074004f0072006400650072000000\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"23ada07f5261f12f34a0bd8e46760962d6b4d576a416f1fea1c64bc656b1d28eacf7047ae6e967c58fd2a98bfa74c298\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c08000000000000003e0000000000000042006f006f0074003000300030003000090100002c0055006900410070007000000004071400c9bdb87cebf8344faaea3ee4af6516a10406140021aa2c4614760345836e8ab6f46623317fff0400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71\",\"event\":\"\",\"event_payload\":\"43616c6c696e6720454649204170706c69636174696f6e2066726f6d20426f6f74204f7074696f6e\"},{\"imr\":1,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":2,\"event_type\":6,\"digest\":\"9c824b958dbbef07ec732fd79727e995d5eca309dbc6d62e4fd2840f267c5e87108b5fe25c7997c67801ff391fb67ffc\",\"event\":\"\",\"event_payload\":\"ed223b8f1a0000004c4f414445445f494d4147453a3a4c6f61644f7074696f6e7300\"},{\"imr\":2,\"event_type\":6,\"digest\":\"5eb4f34f2ec49071551c5562ebaab9f86408f4a0eadaa3d7f05157e37ab04a1f040a82eaae94015d628a7ea4aa482cd2\",\"event\":\"\",\"event_payload\":\"ec223b8f0d0000004c696e757820696e6974726400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0\",\"event\":\"\",\"event_payload\":\"4578697420426f6f7420536572766963657320496e766f636174696f6e\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74\",\"event\":\"\",\"event_payload\":\"4578697420426f6f742053657276696365732052657475726e656420776974682053756363657373\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"f9974020ef507068183313d0ca808e0d1ca9b2d1ad0c61f5784e7157c362c06536f5ddacdad4451693f48fcc72fff624\",\"event\":\"system-preparing\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"8562fc3980258ea5119a31356844547c42ba696d73f6ecdc7d29732beef0083f22674ce14349a8a1400254466e851407\",\"event\":\"app-id\",\"event_payload\":\"fdb7a14e5a6675f752e2cb69c9067a98ca402918\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"3b496ff216c671138e922080e66deabce8a85c8cb628789afa4a93f47566d842d55308dd8cc0f92ab38f1a2578563ee7\",\"event\":\"compose-hash\",\"event_payload\":\"ce26ab560d466475b6778456f406dd73da55a3f94dbd1cffc141db63527d3bd8\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"dbb7d7c2c2ce19938fb5989949f2c4bba6829ced5cd7b8c0d98696c293ac1c5dc8b1bfb9bc95b61e2ff778a3686651eb\",\"event\":\"instance-id\",\"event_payload\":\"15e847e02a22706121de347498427469d2e7c379\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"98bd7e6bd3952720b65027fd494834045d06b4a714bf737a06b874638b3ea00ff402f7f583e3e3b05e921c8570433ac6\",\"event\":\"boot-mr-done\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"65828c6f922910fd0e272501a8b2bb6b03e24c99bb19d1cd530586061d8738f9dd0564752db23d556a6b381cbf465a85\",\"event\":\"mr-kms\",\"event_payload\":\"692afc6d7a86a32cfc1ebd9cad1a576aab012bab46986ba609bc8d6407270572\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"c936f709860155a455c519b505fe29bde21d1b778534a8f0b41b706b0f789eae2ea899e224fedb2331c18324aab4db93\",\"event\":\"os-image-hash\",\"event_payload\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"83368b43a0fc6f824f5a9220592df85fd30e2d405ecbd253a5c6354af63e6c9b41aec557c38a38e348ab87f9ac8fc68c\",\"event\":\"key-provider\",\"event_payload\":\"7b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343838343465623432636364663863353266643466313734663336326663623962626431396334356664343866316564656332643866316361323335333665633161373430323162346365653631306330373466383239346434333162326237666565326333396535333333666461663061343532326434336662313539643966227d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"ba51104636900268b0e059fa3d266419d079d1e94aea26fb9fcbb8d764bf4c89a67ac271b8a0d1a3989945132a111fc7\",\"event\":\"storage-fs\",\"event_payload\":\"7a6673\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"1a76b2a80a0be71eae59f80945d876351a7a3fb8e9fd1ff1cede5734aa84ea11fd72b4edfbb6f04e5a85edd114c751bd\",\"event\":\"system-ready\",\"event_payload\":\"\"}]","vm_config":"{\"os_image_hash\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\",\"cpu_count\":16,\"memory_size\":34359738368,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.5.9-bd369a8c\",\"host_share_mode\":\"9p\",\"spec_version\":1}","key_custody":{"provider":"dstack-kms","keys":[{"role":"receipt","path":"aci/receipt-ed25519/v1","purpose":"aci.receipt.ed25519.v1","algo":"ed25519","public_key":"110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a","kms_public_key":"02f7fb3ed8c352d316dfdc19458a9f29c06eb7e74130b3808d784e2fd401a2c12d","signature_chain":["d1f59335effc2688acdf502ee34d8d3373b0e7e6be493075383714ce5673a29973f224690437c483ae3fdd37172df3abad82877c4023ed7c5a64d5acaf18477c00","5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]},{"role":"e2ee-secp256k1","path":"aci/e2ee/v1","purpose":"aci.e2ee.v1","algo":"secp256k1-aes-256-gcm-hkdf-sha256","public_key":"04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d","kms_public_key":"03943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f","signature_chain":["452d6f86a807fef0ce0d65e6c5cd25a0a249b1fd8608d26c3efe17fc10346f4c2d3dfb8969d10dc1feac1d595b3816f63faa40ac370a3a233ce1d712d418c89701","5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]},{"role":"e2ee-x25519","path":"aci/e2ee-x25519/v1","purpose":"aci.e2ee.x25519.v1","algo":"x25519-aes-256-gcm-hkdf-sha256","public_key":"408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537","kms_public_key":"03892f86b3f55f78cc0760cec933cf8e2a92862fb173c2d221cc25895847cbb5eb","signature_chain":["358cd661ca4f5e986ed950ca083bf7bcfef6ab4b839ca11a953b156d64f82b1c51a213730d5777b4c845170827680d41e8220c2f950a38081025ff16eb3789ba00","5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}]},"downstream_tls_binding":{"domain":"tee.redpill.ai","spki_sha256":"22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b"},"app_compose_b64":"eyJhbGxvd2VkX2VudnMiOlsiUFJJVkFURV9BSV9HQVRFV0FZX0FETUlOX1RPS0VOIiwiUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVVJMIiwiUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVE9LRU4iLCJEU1RBQ0tfUk9PVF9QVUJMSUNfS0VZIiwiQ0xPVURGTEFSRV9BUElfVE9LRU4iLCJDRVJUQk9UX0VNQUlMIiwiTk9ERV9FWFBPUlRFUl9VU0VSIiwiTk9ERV9FWFBPUlRFUl9QQVNTV09SRF9IQVNIIl0sImRvY2tlcl9jb21wb3NlX2ZpbGUiOiIjIGRzdGFjayAvIFBoYWxhIGRlcGxveW1lbnQgbWFuaWZlc3QgZm9yIHRoZSBwcml2YXRlLWFpLWdhdGV3YXkgc3RhY2sgdmlhIHRoZSBsYXVuY2hlci5cbiNcbiMgVGhlIGxhdW5jaGVyIGltYWdlIGlzIHRoZSBtZWFzdXJlZCB0cnVzdC1yb290OiBwaW4gaXQgYnkgQHNoYTI1NiBkaWdlc3QgKHRoZVxuIyBkaWdlc3QgKyB0aGlzIGZpbGUgYXJlIGhhc2hlZCBpbnRvIHRoZSBURFggcXVvdGUpLiBUaGUgbGF1bmNoZXIgY2xvbmVzK2J1aWxkc1xuIyB0aGUgZ2F0ZXdheSBmcm9tIHNvdXJjZSBhdCBib290OyB0aGUgYXR0ZXN0YXRpb24gdmVyaWZpZXIgaXNcbiMgdGhlIGdhdGV3YXkncyB2ZW5kb3JlZCBjb25maWRlbnRpYWxfdmVyaWZpZXIgcGFja2FnZSwgc28gaXQgbmVlZHMgbm8gc2VwYXJhdGUgcGluLlxuI1xuIyBUaGUgZ2F0ZXdheSdzIHNvdXJjZSBwaW4gKyBzdGF0aWMgY29uZmlnIGFyZSBpbmxpbmVkIGFzIGNvbXBvc2UgYGNvbmZpZ3M6YFxuIyAobWVhc3VyZWQgaW50byB0aGUgYXR0ZXN0YXRpb24pOiB0aGUgbGF1bmNoZXIgY2xvbmVzIHRoZSBwaW4gYW5kIHRoZSBnYXRld2F5XG4jIHJlYWRzIHRoZSBjb25maWcgZnJvbSBtb3VudGVkIGZpbGVzLiBTZWNyZXQgdmFsdWVzIGFyZSBzdXBwbGllZCBhcyBkc3RhY2tcbiMgRU5DUllQVEVEIFNFQ1JFVFMgYW5kIHJlZmVyZW5jZWQgYXMgJHtWQVJ9IHNvIGRzdGFjayBpbmplY3RzIHRoZW0g4oCUIG9ubHkgdGhlXG4jIHZhcmlhYmxlIG5hbWVzIGFwcGVhciBpbiB0aGlzIHB1YmxpYyBmaWxlLCBuZXZlciB0aGUgdmFsdWVzLlxuXG5zZXJ2aWNlczpcbiAgIyBUTFMtdGVybWluYXRpbmcgaW5ncmVzczogcmVxdWVzdHMgQ0FBLXBpbm5lZCBMZXQncyBFbmNyeXB0IGNlcnRzIHZpYSBDbG91ZGZsYXJlXG4gICMgRE5TLTAxLCByZWdpc3RlcnMgdW5kZXIgdGhlIGRzdGFjayBnYXRld2F5IGRvbWFpbiwgYW5kIHJldmVyc2UtcHJveGllcyBpbmJvdW5kXG4gICMgNDQzIHRvIHRoZSBsYXVuY2hlcidzIGdhdGV3YXkgb24gOjgwODYuIFBpbiBieSBAc2hhMjU2IGRpZ2VzdCBsaWtlIHRoZSBsYXVuY2hlci5cbiAgZHN0YWNrLWluZ3Jlc3M6XG4gICAgaW1hZ2U6IGRzdGFja3RlZS9kc3RhY2staW5ncmVzczoyLjJAc2hhMjU2OmQwNWE3YjM0M2MzN2MxY2NhMWJiYThkYmY3ZThmM2M2ZDIxMTgxNThhZjJkNDFjNDU1MTAzNzk2ZGI0ZjY3ZjBcbiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZFxuICAgIHBvcnRzOlxuICAgICAgLSBcIjQ0Mzo0NDNcIlxuICAgIGVudmlyb25tZW50OlxuICAgICAgIyAtLS0gcHVibGljIGNvbmZpZzogbGl0ZXJhbHMsIG1lYXN1cmVkIGludG8gdGhlIGF0dGVzdGF0aW9uIC0tLVxuICAgICAgRE5TX1BST1ZJREVSOiBjbG91ZGZsYXJlXG4gICAgICBHQVRFV0FZX0RPTUFJTjogXy4ke0RTVEFDS19HQVRFV0FZX0RPTUFJTn1cbiAgICAgIFNFVF9DQUE6IFwidHJ1ZVwiXG4gICAgICAjIE11bHRpLWRvbWFpbiBUTFMuIFRoZSBpbmdyZXNzIGlzIGFuIEw0IChUQ1ApIHByb3h5OiBpdCB0ZXJtaW5hdGVzIFRMUyBmb3JcbiAgICAgICMgZWFjaCBob3N0bmFtZSBhbmQgZm9yd2FyZHMgdGhlIERFQ1JZUFRFRCBzdHJlYW0gdW50b3VjaGVkLCBzbyB0aGUgY2xpZW50J3NcbiAgICAgICMgb3JpZ2luYWwgSFRUUCBIb3N0IHN1cnZpdmVzLiBUaGUgZ2F0ZXdheSB1c2VzIHRoYXQgSG9zdCB0byBwaWNrIHRoZVxuICAgICAgIyBtYXRjaGluZyBkb3duc3RyZWFtX3Rsc19iaW5kaW5nLiBCb3RoIHB1YmxpYyBob3N0bmFtZXMgcm91dGUgYnkgU05JIHRvIHRoZVxuICAgICAgIyBzYW1lIGdhdGV3YXkgbGlzdGVuZXIgKGNvbXBvc2Ugc2VydmljZSBuYW1lLCBub3QgbG9jYWxob3N0KS5cbiAgICAgIERPTUFJTlM6IHxcbiAgICAgICAgdGVlLnJlZHBpbGwuYWlcbiAgICAgICAgaW5mZXJlbmNlLnBoYWxhLmNvbVxuICAgICAgICBhcGkucmVkcGlsbC5haVxuICAgICAgUk9VVElOR19NQVA6IHxcbiAgICAgICAgdGVlLnJlZHBpbGwuYWk9bGF1bmNoZXI6ODA4NlxuICAgICAgICBpbmZlcmVuY2UucGhhbGEuY29tPWxhdW5jaGVyOjgwODZcbiAgICAgICAgYXBpLnJlZHBpbGwuYWk9bGF1bmNoZXI6ODA4NlxuICAgICAgIyAtLS0gc2VjcmV0czogdmFsdWVzIGNvbWUgZnJvbSBkc3RhY2sgRW5jcnlwdGVkIFNlY3JldHMgKG5ldmVyIGNvbW1pdCB2YWx1ZXMpIC0tLVxuICAgICAgIyBOT1RFOiB0aGlzIHNpbmdsZSBDbG91ZGZsYXJlIHRva2VuIG11c3QgaGF2ZSBETlMtZWRpdCAoYW5kIENBQSkgcmlnaHRzIG9uXG4gICAgICAjIEJPVEggdGhlIHJlZHBpbGwuYWkgYW5kIHBoYWxhLmNvbSB6b25lcywgb3IgY2VydCBpc3N1YW5jZSBmb3Igb25lIGZhaWxzLlxuICAgICAgQ0xPVURGTEFSRV9BUElfVE9LRU46ICR7Q0xPVURGTEFSRV9BUElfVE9LRU59XG4gICAgICBDRVJUQk9UX0VNQUlMOiAke0NFUlRCT1RfRU1BSUx9XG4gICAgdm9sdW1lczpcbiAgICAgIC0gL3Zhci9ydW4vZHN0YWNrLnNvY2s6L3Zhci9ydW4vZHN0YWNrLnNvY2tcbiAgICAgIC0gL3Zhci9ydW4vdGFwcGQuc29jazovdmFyL3J1bi90YXBwZC5zb2NrXG4gICAgICAtIGNlcnQtZGF0YTovZXRjL2xldHNlbmNyeXB0XG4gICAgICAjIFBlci1kb21haW4gbGVhZiBjZXJ0cyAoY2VydC08ZG9tYWluPi5wZW0pIHRoZSBnYXRld2F5IHJlYWRzIHRvIGJpbmQgU1BLSS5cbiAgICAgIC0gZXZpZGVuY2VzOi9ldmlkZW5jZXNcblxuICBsYXVuY2hlcjpcbiAgICBpbWFnZTogZ2hjci5pby9yZWRwaWxsLWFpL3ByaXZhdGUtYWktbGF1bmNoZXJAc2hhMjU2OmMwODNmZjllNmE1ZGRmMTBmNmM5ZTliYjFmNzRjYzYxOGRlZWJlY2ZlYTUyMDhiNTYzYzU3NDM5OWRiNDYzN2NcbiAgICByZXN0YXJ0OiBhbHdheXNcbiAgICAjIFRoZSBnYXRld2F5IG9wZW5zIG1hbnkgZmRzIChpbmJvdW5kIGNvbm5zLCBwZXItdXBzdHJlYW0gcG9vbHMsIGNvbnRyb2wtcGxhbmVcbiAgICAjICsgdmVyaWZpZXIgY2FsbHMpLiBEb2NrZXIncyBkZWZhdWx0IG5vZmlsZSAob2Z0ZW4gMTAyNCkgaXMgZmFyIHRvbyBsb3c6IHVuZGVyXG4gICAgIyBsb2FkIGl0IGhpdCBFTUZJTEUgKFwiVG9vIG1hbnkgb3BlbiBmaWxlc1wiKSwgd2hpY2gga2lsbGVkIHRoZSBnYXRld2F5J3MgYWNjZXB0XG4gICAgIyBsb29wLiBSYWlzZSB0aGUgbGltaXQgd2VsbCBhYm92ZSBwZWFrIGNvbmN1cnJlbmN5LlxuICAgIHVsaW1pdHM6XG4gICAgICBub2ZpbGU6XG4gICAgICAgIHNvZnQ6IDY1NTM2XG4gICAgICAgIGhhcmQ6IDY1NTM2XG4gICAgIyBzdXBlcnZpc29yZCBzdHJlYW1zIHRoZSBnYXRld2F5IGxvZ3MgdG8gdGhlIGNvbnRhaW5lciBzdGRvdXQ7IGNhcCB0aGVcbiAgICAjIHJ1bnRpbWUgbG9nIG9uIGRpc2sgc28gaXQgY2FuJ3QgZ3JvdyB1bmJvdW5kZWQgKH41R0IgdG90YWwsIHJvdGF0ZWQpLlxuICAgICMgU2l6ZWQgZm9yIHRoZSBnYXRld2F5J3MgYWx3YXlzLW9uIHJlcXVlc3Rfb3V0Y29tZSBmYWlsdXJlIGxvZ2dpbmc6XG4gICAgIyByb3VnaGx5IGEgbW9udGggb2YgcmV0ZW50aW9uIG9uIHRoZSBwZXJzaXN0ZW50IHZvbHVtZSBmb3IgaW5jaWRlbnRcbiAgICAjIGZvcmVuc2ljcy5cbiAgICBsb2dnaW5nOlxuICAgICAgZHJpdmVyOiBqc29uLWZpbGVcbiAgICAgIG9wdGlvbnM6XG4gICAgICAgIG1heC1zaXplOiBcIjUwMG1cIlxuICAgICAgICBtYXgtZmlsZTogXCIxMFwiXG4gICAgIyBubyBob3N0IHBvcnQ6IHRyYWZmaWMgZW50ZXJzIHZpYSBkc3RhY2staW5ncmVzcyAoNDQzKSBhbmQgcmVhY2hlcyB0aGVcbiAgICAjIGdhdGV3YXkgb3ZlciB0aGUgY29tcG9zZSBuZXR3b3JrIGFzIGh0dHA6Ly9sYXVuY2hlcjo4MDg2LlxuICAgIGV4cG9zZTpcbiAgICAgIC0gXCI4MDg2XCJcbiAgICB2b2x1bWVzOlxuICAgICAgLSAvdmFyL3J1bi9kc3RhY2suc29jazovdmFyL3J1bi9kc3RhY2suc29jayAgICMgb25seSB0aGUgZ2F0ZXdheSB1c2VzIHRoaXNcbiAgICAgIC0gcGFsLWNhY2hlOi92YXIvbGliL3BhZy9jYWNoZSAgICAgICAgICAgICAgICAgIyBjYXJnby91diBidWlsZCBjYWNoZSAoc3BlZWRzIHJlYm9vdHMpXG4gICAgICAtIHBhbC1zdGF0ZTovdmFyL2xpYi9wYWcvc3RhdGUgICAgICAgICAgICAgICAgICMgbXV0YWJsZSB1cHN0cmVhbXMuanNvbiArIHNlc3Npb25zLmpzb25sXG4gICAgICAtIGV2aWRlbmNlczovZXZpZGVuY2VzOnJvICAgICAgICAgICAgICAgICAgICAgICMgaW5ncmVzcy1taW50ZWQgcGVyLWRvbWFpbiBsZWFmIGNlcnRzIChjZXJ0LTxkb21haW4+LnBlbSlcbiAgICAjIFN0YXRpYyBnYXRld2F5IGNvbmZpZyArIHVwc3RyZWFtIHNlZWQgYXJlIG1vdW50ZWQgZnJvbSB0aGUgYGNvbmZpZ3M6YCBibG9ja1xuICAgICMgYXQgdGhlIGJvdHRvbSAobWVhc3VyZWQgaW50byB0aGUgYXR0ZXN0YXRpb24pLiBUaGUgc291cmNlIHBpbiBpcyBwYXNzZWQgdmlhXG4gICAgIyBgZW52aXJvbm1lbnQ6YCBpbnN0ZWFkIChzZWUgYmVsb3cpLlxuICAgIGNvbmZpZ3M6XG4gICAgICAtIHNvdXJjZTogZ2F0ZXdheS1jb25maWdcbiAgICAgICAgdGFyZ2V0OiAvZXRjL3ByaXZhdGUtYWktZ2F0ZXdheS9nYXRld2F5LmNvbmZpZy5qc29uXG4gICAgICAtIHNvdXJjZTogZ2F0ZXdheS11cHN0cmVhbXNcbiAgICAgICAgdGFyZ2V0OiAvZXRjL3ByaXZhdGUtYWktZ2F0ZXdheS91cHN0cmVhbXMuc2VlZC5qc29uXG4gICAgZW52aXJvbm1lbnQ6XG4gICAgICAjIFRoZSBnYXRld2F5IHJlYWRzIGl0cyB3aG9sZSBwb2xpY3kgZnJvbSB0aGlzIG1vdW50ZWQgc3RhdGljIGNvbmZpZyBmaWxlXG4gICAgICAjIChzZWUgYGNvbmZpZ3M6YCBhYm92ZSk7IENPTkZJR19QQVRIIGlzIHRoZSBvbmx5IGdhdGV3YXktZmFjaW5nIGVudiB2YXIuXG4gICAgICAtIFBSSVZBVEVfQUlfR0FURVdBWV9DT05GSUdfUEFUSD0vZXRjL3ByaXZhdGUtYWktZ2F0ZXdheS9nYXRld2F5LmNvbmZpZy5qc29uXG4gICAgICAjIFRyYWNpbmcgZmlsdGVyLiBgcmVxdWVzdF9vdXRjb21lPWRlYnVnYCB0dXJucyBvbiB0aGUgcmF3IGVycm9yLWRldGFpbFxuICAgICAgIyBmaWVsZCBvZiB0aGUgcGVyLXJlcXVlc3QgZmFpbHVyZSBsb2cg4oCUIGEgZGVsaWJlcmF0ZSBvcGVyYXRvciBjaG9pY2U6XG4gICAgICAjIHVwc3RyZWFtIGVycm9yIGJvZGllcyBtYXkgZWNobyByZXF1ZXN0IGZyYWdtZW50cyBpbnRvIHRoZXNlXG4gICAgICAjIG9wZXJhdG9yLXZpc2libGUgbG9ncy4gRHJvcCB0aGUgYHJlcXVlc3Rfb3V0Y29tZT1kZWJ1Z2AgcGFydCB0byBmYWxsXG4gICAgICAjIGJhY2sgdG8gY29udGVudC1mcmVlIGZhaWx1cmUgbG9nZ2luZyBvbmx5LlxuICAgICAgLSBSVVNUX0xPRz1pbmZvLHJlcXVlc3Rfb3V0Y29tZT1kZWJ1Z1xuICAgICAgIyBHYXRld2F5IHNvdXJjZSBwaW4uIFRoZXNlIGxpdGVyYWxzIGFyZSBtZWFzdXJlZCBpbnRvIHRoZSBhdHRlc3RhdGlvbiwgYW5kXG4gICAgICAjIGJlY2F1c2UgdGhleSBsaXZlIGluIGBlbnZpcm9ubWVudDpgIChwYXJ0IG9mIENvbXBvc2UncyBjb25maWctaGFzaCkgYVxuICAgICAgIyBDT01NSVRfU0hBIGJ1bXAgZm9yY2VzIGEgY29udGFpbmVyIHJlY3JlYXRlIG9uIGBwaGFsYSBkZXBsb3lgIOKAlCBzbyB0aGUgbmV3XG4gICAgICAjIHBpbiBhY3R1YWxseSB0YWtlcyBlZmZlY3QuIChBIGBjb25maWdzOmAtZmlsZSBwaW4gd291bGQgbm90OiBDb21wb3NlIGlnbm9yZXNcbiAgICAgICMgY29uZmlnIGNvbnRlbnQgd2hlbiBkZWNpZGluZyB0byByZWNyZWF0ZSwgc28gdGhlIGNoYW5nZSB3b3VsZCBuZXZlciBhcHBseS4pXG4gICAgICAjIGVudHJ5cG9pbnQuc2ggd3JpdGVzIC9ldGMvZ2l0LWxhdW5jaGVyL2dhdGV3YXkuY29uZiBmcm9tIHRoZXNlLCBhbmQgdGhlXG4gICAgICAjIGdhdGV3YXkgcmVhZHMgdGhhdCBmaWxlIGFzIGl0cyBhdHRlc3RlZCBzb3VyY2UgcHJvdmVuYW5jZS5cbiAgICAgIC0gUFJJVkFURV9BSV9HQVRFV0FZX1JFUE9fVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9Ec3RhY2stVEVFL3ByaXZhdGUtYWktZ2F0ZXdheS5naXRcbiAgICAgIC0gUFJJVkFURV9BSV9HQVRFV0FZX1JFUE9fQ09NTUlUPWI2YjVjMWI4MmY2ZmM1OTQ5MGRiNWE1MjU1YmY0NDkzODA1ZTY2YzZcbiAgICAgICMgTkVBUiBBSSB2ZXJpZmljYXRpb24gcG9zdHMgVERYIHF1b3RlcyB0byB0aGUgZHN0YWNrLXZlcmlmaWVyIHNpZGVjYXIgYmVsb3c7XG4gICAgICAjIHJlYWNoIGl0IGJ5IGNvbXBvc2Ugc2VydmljZSBuYW1lIChsb2NhbGhvc3Qgd291bGQgYmUgdGhlIGxhdW5jaGVyIGl0c2VsZikuXG4gICAgICAtIERTVEFDS19WRVJJRklFUl9VUkw9aHR0cDovL2RzdGFjay12ZXJpZmllcjo4MDgwXG4gICAgICAjIC0tLSBzZWNyZXRzOiB2YWx1ZXMgY29tZSBmcm9tIGRzdGFjayBFbmNyeXB0ZWQgU2VjcmV0cyAobmV2ZXIgY29tbWl0IHZhbHVlcykgLS0tXG4gICAgICAjIENvbnRyb2wtcGxhbmUgY3JlZGVudGlhbHMuIFRoZSBnYXRld2F5J3MgaW4tcHJvY2VzcyBtaWRkbGV3YXJlIHJlYWRzIHRoZXNlXG4gICAgICAjIGZyb20gdGhlIGBtaWRkbGV3YXJlYCBjb25maWcgc2VjdGlvbiBiZWxvdyAocmVuZGVyZWQgZnJvbSB0aGUgc2FtZSBzZWNyZXRzKS5cbiAgICAgIC0gUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVVJMPSR7UFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVVJMfVxuICAgICAgLSBQUklWQVRFX0FJX0dBVEVXQVlfQ09OVFJPTF9UT0tFTj0ke1BSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1RPS0VOfVxuXG4gICMgVERYIHF1b3RlIHZlcmlmaWVyIGZvciB0aGUgTkVBUiBBSSBwYXRoIChyZXByb2R1Y2VzIEFDUEkvYm9vdCBtZWFzdXJlbWVudHMgdmlhXG4gICMgUUVNVSkuIFRoZSBnYXRld2F5IHJlYWNoZXMgaXQgYXQgaHR0cDovL2RzdGFjay12ZXJpZmllcjo4MDgwIChEU1RBQ0tfVkVSSUZJRVJfVVJMKS5cbiAgZHN0YWNrLXZlcmlmaWVyOlxuICAgIGltYWdlOiBkc3RhY2t0ZWUvZHN0YWNrLXZlcmlmaWVyOjAuNS4xMUBzaGEyNTY6MDZhMjBiNzc3ZTU5MTk2YzdmNTRiMGNkMDJmZmU1NjdkZjZkMDVhZTM5YTBiMmZlYTljYjA2NDJmYjIzYzFmZFxuICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkXG4gICAgbG9nZ2luZzpcbiAgICAgIGRyaXZlcjoganNvbi1maWxlXG4gICAgICBvcHRpb25zOlxuICAgICAgICBtYXgtc2l6ZTogXCI1MG1cIlxuICAgICAgICBtYXgtZmlsZTogXCI1XCJcblxuICAjIEhvc3QtbWV0cmljcyBleHBvcnRlciBmb3Igb2JzZXJ2aW5nIHRoaXMgQ1ZNIChDUFUvbWVtL2Rpc2svbmV0L2ZkKS4gSW5zaWRlIGFcbiAgIyBDVk0gdGhlIGNvbnRhaW5lcidzIC8sIC9wcm9jLCAvc3lzIEFSRSB0aGUgZ3Vlc3Qncywgc28gbm9kZS1leHBvcnRlciByZXBvcnRzXG4gICMgdGhlIENWTSdzIG93biBzdGF0ZS4gUGlubmVkIGJ5IEBzaGEyNTYgbGlrZSB0aGUgb3RoZXIgaW1hZ2VzIChtZWFzdXJlZCBpbnRvXG4gICMgdGhlIGF0dGVzdGF0aW9uKS4gRXhwb3NlZCBvbiA5MTAwIChyZWFjaGVkIHZpYSB0aGUgZHN0YWNrIGdhdGV3YXkpIGFuZCBndWFyZGVkXG4gICMgYnkgSFRUUCBiYXNpYyBhdXRoIOKAlCBib3RoIHVzZXJuYW1lIGFuZCB0aGUgYmNyeXB0IHBhc3N3b3JkIEhBU0ggYXJlIGluamVjdGVkIGFzXG4gICMgZHN0YWNrIEVuY3J5cHRlZCBTZWNyZXRzLCBzbyBuZWl0aGVyIGFwcGVhcnMgaW4gdGhpcyBtZWFzdXJlZCBmaWxlLlxuICBub2RlLWV4cG9ydGVyOlxuICAgIGltYWdlOiBwcm9tL25vZGUtZXhwb3J0ZXI6djEuOC4yQHNoYTI1Njo0MDMyYzZkNWJmZDc1MjM0MmMzZTYzMWMyZjFkZTkzYmE2Yjg2YzQxZGI2YjE2N2I5YTM1MzcyYzEzOWU3NzA2XG4gICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWRcbiAgICBwaWQ6IGhvc3RcbiAgICBjb21tYW5kOlxuICAgICAgLSAnLS1wYXRoLnJvb3Rmcz0vaG9zdCdcbiAgICAgIC0gJy0tcGF0aC5wcm9jZnM9L2hvc3QvcHJvYydcbiAgICAgIC0gJy0tcGF0aC5zeXNmcz0vaG9zdC9zeXMnXG4gICAgICAtICctLXdlYi5jb25maWcuZmlsZT0vZXRjL25vZGUtZXhwb3J0ZXIvd2ViLWNvbmZpZy55bWwnXG4gICAgdm9sdW1lczpcbiAgICAgIC0gLzovaG9zdDpybyxyc2xhdmVcbiAgICBjb25maWdzOlxuICAgICAgLSBzb3VyY2U6IG5vZGUtZXhwb3J0ZXItd2ViLWNvbmZpZ1xuICAgICAgICB0YXJnZXQ6IC9ldGMvbm9kZS1leHBvcnRlci93ZWItY29uZmlnLnltbFxuICAgIHBvcnRzOlxuICAgICAgLSBcIjkxMDA6OTEwMFwiXG4gICAgbG9nZ2luZzpcbiAgICAgIGRyaXZlcjoganNvbi1maWxlXG4gICAgICBvcHRpb25zOlxuICAgICAgICBtYXgtc2l6ZTogXCIxMG1cIlxuICAgICAgICBtYXgtZmlsZTogXCIzXCJcblxudm9sdW1lczpcbiAgcGFsLWNhY2hlOlxuICBwYWwtc3RhdGU6XG4gIGNlcnQtZGF0YTogICAjIExldCdzIEVuY3J5cHQgY2VydHMva2V5cyBwZXJzaXN0ZWQgZm9yIGRzdGFjay1pbmdyZXNzXG4gIGV2aWRlbmNlczogICAjIGluZ3Jlc3MgYXR0ZXN0YXRpb24gZXZpZGVuY2UgKyBwZXItZG9tYWluIGxlYWYgY2VydHMsIHNoYXJlZCByZWFkLW9ubHkgd2l0aCB0aGUgZ2F0ZXdheVxuXG4jIElubGluZWQsIG1lYXN1cmVkIGludG8gdGhlIGF0dGVzdGF0aW9uLiBQdWJsaWMgcGlucyB1c2UgTElURVJBTCB2YWx1ZXMgc28gdGhlXG4jIGNvZGUgdmVyc2lvbiBpcyBib3VuZCBpbnRvIGNvbXBvc2VfaGFzaDsgb25seSBzZWNyZXRzIHVzZSAke1ZBUn0gKGluamVjdGVkIGJ5XG4jIGRzdGFjaywgbmV2ZXIgbWVhc3VyZWQpLlxuY29uZmlnczpcbiAgIyBTdGF0aWMgZ2F0ZXdheSBjb25maWcuIGBhZG1pbl90b2tlbmAgYW5kIHRoZSBjb250cm9sLXBsYW5lIGNyZWRlbnRpYWxzIGFyZSB0aGVcbiAgIyBvbmx5IHNlY3JldHMgaGVyZSDigJQgJHtWQVJ9IGtlZXBzIHRoZW0gb3V0IG9mIHRoZSBtZWFzdXJlZCBjb21wb3NlLiBUaGVcbiAgIyBgbWlkZGxld2FyZWAgc2VjdGlvbiBydW5zIHRoZSBpbi1wcm9jZXNzIG1pZGRsZXdhcmUsIHdoaWNoIGNvbnN1bHRzIHRoZSBjb250cm9sXG4gICMgcGxhbmUgYXQgYGNvbnRyb2xfdXJsYC5cbiAgZ2F0ZXdheS1jb25maWc6XG4gICAgY29udGVudDogfFxuICAgICAge1xuICAgICAgICBcImJpbmRcIjogXCIwLjAuMC4wOjgwODZcIixcbiAgICAgICAgXCJzdGF0ZV9kaXJcIjogXCIvdmFyL2xpYi9wYWcvc3RhdGVcIixcbiAgICAgICAgXCJ1cHN0cmVhbV9jb25maWdfc2VlZF9wYXRoXCI6IFwiL2V0Yy9wcml2YXRlLWFpLWdhdGV3YXkvdXBzdHJlYW1zLnNlZWQuanNvblwiLFxuICAgICAgICBcImFkbWluX3Rva2VuXCI6IFwiJHtQUklWQVRFX0FJX0dBVEVXQVlfQURNSU5fVE9LRU46P3NldCBQUklWQVRFX0FJX0dBVEVXQVlfQURNSU5fVE9LRU59XCIsXG4gICAgICAgIFwidGxzXCI6IHtcbiAgICAgICAgICBcImRvbWFpbl9jZXJ0aWZpY2F0ZXNcIjogW1xuICAgICAgICAgICAgeyBcImRvbWFpblwiOiBcInRlZS5yZWRwaWxsLmFpXCIsIFwiY2VydGlmaWNhdGVfcGF0aFwiOiBcIi9ldmlkZW5jZXMvY2VydC10ZWUucmVkcGlsbC5haS5wZW1cIiB9LFxuICAgICAgICAgICAgeyBcImRvbWFpblwiOiBcImluZmVyZW5jZS5waGFsYS5jb21cIiwgXCJjZXJ0aWZpY2F0ZV9wYXRoXCI6IFwiL2V2aWRlbmNlcy9jZXJ0LWluZmVyZW5jZS5waGFsYS5jb20ucGVtXCIgfSxcbiAgICAgICAgICAgIHsgXCJkb21haW5cIjogXCJhcGkucmVkcGlsbC5haVwiLCBcImNlcnRpZmljYXRlX3BhdGhcIjogXCIvZXZpZGVuY2VzL2NlcnQtYXBpLnJlZHBpbGwuYWkucGVtXCIgfVxuICAgICAgICAgIF1cbiAgICAgICAgfSxcbiAgICAgICAgXCJtaWRkbGV3YXJlXCI6IHtcbiAgICAgICAgICBcImNvbnRyb2xfdXJsXCI6IFwiJHtQUklWQVRFX0FJX0dBVEVXQVlfQ09OVFJPTF9VUkw6P3NldCBQUklWQVRFX0FJX0dBVEVXQVlfQ09OVFJPTF9VUkx9XCIsXG4gICAgICAgICAgXCJjb250cm9sX3Rva2VuXCI6IFwiJHtQUklWQVRFX0FJX0dBVEVXQVlfQ09OVFJPTF9UT0tFTjo/c2V0IFBSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1RPS0VOfVwiLFxuICAgICAgICAgIFwidGVlX29ubHlfZG9tYWluc1wiOiBbXCJ0ZWUucmVkcGlsbC5haVwiLCBcImluZmVyZW5jZS5waGFsYS5jb21cIl1cbiAgICAgICAgfVxuICAgICAgfVxuICAjIEluaXRpYWwgdXBzdHJlYW0gcm91dGVzOiBzZWVkZWQgaW50byA8c3RhdGVfZGlyPi91cHN0cmVhbXMuanNvbiBvbmx5IHdoZW4gZW1wdHksXG4gICMgdGhlbiBtYW5hZ2VkIGF0IHJ1bnRpbWUgdmlhIFBVVCAvdjEvYWRtaW4vdXBzdHJlYW1zIChkb2VzIG5vdCB0b3VjaCB0aGlzIGZpbGUpLlxuICBnYXRld2F5LXVwc3RyZWFtczpcbiAgICBjb250ZW50OiB8XG4gICAgICBbXVxuICAjIG5vZGUtZXhwb3J0ZXIgYmFzaWMtYXV0aCBjcmVkZW50aWFscy4gZXhwb3J0ZXItdG9vbGtpdCdzIGJhc2ljX2F1dGhfdXNlcnMgbWFwc1xuICAjIHVzZXJuYW1lIC0+IGJjcnlwdCBoYXNoOyBib3RoIGFyZSAke1ZBUn0gc28gZHN0YWNrIHN1YnN0aXR1dGVzIHRoZSBpbmplY3RlZFxuICAjIHNlY3JldCB2YWx1ZXMgd2hlbiByZW5kZXJpbmcgdGhpcyBmaWxlIGludG8gdGhlIGNvbnRhaW5lciDigJQgb25seSB0aGUgbGl0ZXJhbHNcbiAgIyBhcmUgbWVhc3VyZWQuIE5PREVfRVhQT1JURVJfUEFTU1dPUkRfSEFTSCBtdXN0IGJlIGEgYmNyeXB0IGhhc2hcbiAgIyAoYGh0cGFzc3dkIC1uQkMgMTAgXCJcIiB8IHRyIC1kICc6XFxuJ2ApLCBOT1QgdGhlIHBsYWludGV4dCBwYXNzd29yZDsgdGhlIHBsYWludGV4dFxuICAjIGdvZXMgdG8gdGhlIGV4dGVybmFsIFByb21ldGhldXMgc2NyYXBlIGNvbmZpZy5cbiAgbm9kZS1leHBvcnRlci13ZWItY29uZmlnOlxuICAgIGNvbnRlbnQ6IHxcbiAgICAgIGJhc2ljX2F1dGhfdXNlcnM6XG4gICAgICAgICR7Tk9ERV9FWFBPUlRFUl9VU0VSOj9zZXQgTk9ERV9FWFBPUlRFUl9VU0VSfTogJHtOT0RFX0VYUE9SVEVSX1BBU1NXT1JEX0hBU0g6P3NldCBOT0RFX0VYUE9SVEVSX1BBU1NXT1JEX0hBU0h9XG4iLCJmZWF0dXJlcyI6WyJrbXMiLCJ0cHJveHktbmV0Il0sImdhdGV3YXlfZW5hYmxlZCI6dHJ1ZSwia21zX2VuYWJsZWQiOnRydWUsImxvY2FsX2tleV9wcm92aWRlcl9lbmFibGVkIjpmYWxzZSwibWFuaWZlc3RfdmVyc2lvbiI6MiwibmFtZSI6IiIsIm5vX2luc3RhbmNlX2lkIjpmYWxzZSwicHJlX2xhdW5jaF9zY3JpcHQiOiIjIS9iaW4vYmFzaFxuZWNobyBcIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cIlxuZWNobyBcIlJ1bm5pbmcgUGhhbGEgQ2xvdWQgUHJlLUxhdW5jaCBTY3JpcHQgdjAuMC4xNVwiXG5lY2hvIFwiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVwiXG5zZXQgLWVcblxuIyBGdW5jdGlvbjogbm90aWZ5IGhvc3Rcblxubm90aWZ5X2hvc3QoKSB7XG4gICAgaWYgY29tbWFuZCAtdiBkc3RhY2stdXRpbCA+L2Rldi9udWxsIDI+JjE7IHRoZW5cbiAgICAgICAgZHN0YWNrLXV0aWwgbm90aWZ5LWhvc3QgLWUgXCIkMVwiIC1kIFwiJDJcIlxuICAgIGVsc2VcbiAgICAgICAgdGR4Y3RsIG5vdGlmeS1ob3N0IC1lIFwiJDFcIiAtZCBcIiQyXCJcbiAgICBmaVxufVxuXG5ub3RpZnlfaG9zdF9ob290X2luZm8oKSB7XG4gICAgbm90aWZ5X2hvc3QgXCJib290LnByb2dyZXNzXCIgXCIkMVwiXG59XG5cbm5vdGlmeV9ob3N0X2hvb3RfZXJyb3IoKSB7XG4gICAgbm90aWZ5X2hvc3QgXCJib290LmVycm9yXCIgXCIkMVwiXG59XG5cbiMgRnVuY3Rpb246IFBlcmZvcm0gRG9ja2VyIGNsZWFudXBcbnBlcmZvcm1fY2xlYW51cCgpIHtcbiAgICBlY2hvIFwiUHJ1bmluZyB1bnVzZWQgaW1hZ2VzXCJcbiAgICBkb2NrZXIgaW1hZ2UgcHJ1bmUgLWFmXG4gICAgZWNobyBcIlBydW5pbmcgdW51c2VkIHZvbHVtZXNcIlxuICAgIGRvY2tlciB2b2x1bWUgcHJ1bmUgLWZcbiAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJkb2NrZXIgY2xlYW51cCBjb21wbGV0ZWRcIlxufVxuXG4jIEZ1bmN0aW9uOiBDaGVjayBEb2NrZXIgbG9naW4gc3RhdHVzIHdpdGhvdXQgZXhwb3NpbmcgY3JlZGVudGlhbHNcbmNoZWNrX2RvY2tlcl9sb2dpbigpIHtcbiAgICBsb2NhbCByZWdpc3RyeT1cIiQxXCJcblxuICAgICMgV2hlbiByZWdpc3RyeSBpcyBzcGVjaWZpZWQsIGNoZWNrIGF1dGggZW50cnkgZm9yIHRoYXQgcmVnaXN0cnkgaW4gRG9ja2VyIGNvbmZpZ1xuICAgIGlmIFtbIC1uIFwiJHJlZ2lzdHJ5XCIgXV07IHRoZW5cbiAgICAgICAgbG9jYWwgZG9ja2VyX2NvbmZpZ19wYXRoPVwiJHtET0NLRVJfQ09ORklHOi0kSE9NRS8uZG9ja2VyfS9jb25maWcuanNvblwiXG4gICAgICAgIGlmIFtbIC1mIFwiJGRvY2tlcl9jb25maWdfcGF0aFwiIF1dICYmIGdyZXAgLXEgXCIkcmVnaXN0cnlcIiBcIiRkb2NrZXJfY29uZmlnX3BhdGhcIjsgdGhlblxuICAgICAgICAgICAgcmV0dXJuIDBcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgcmV0dXJuIDFcbiAgICAgICAgZmlcbiAgICBmaVxuXG4gICAgIyBGYWxsYmFjayBjaGVjayB3aGVuIG5vIGV4cGxpY2l0IHJlZ2lzdHJ5IGlzIHByb3ZpZGVkXG4gICAgaWYgZG9ja2VyIGluZm8gMj4vZGV2L251bGwgfCBncmVwIC1xIFwiVXNlcm5hbWVcIjsgdGhlblxuICAgICAgICByZXR1cm4gMFxuICAgIGVsc2VcbiAgICAgICAgcmV0dXJuIDFcbiAgICBmaVxufVxuXG4jIE1haW4gbG9naWMgc3RhcnRzIGhlcmVcbmVjaG8gXCJTdGFydGluZyBsb2dpbiBwcm9jZXNzLi4uXCJcblxuIyBDaGVjayBpZiBEb2NrZXIgY3JlZGVudGlhbHMgZXhpc3RcbmlmIFtbIC1uIFwiJERTVEFDS19ET0NLRVJfVVNFUk5BTUVcIiAmJiAtbiBcIiREU1RBQ0tfRE9DS0VSX1BBU1NXT1JEXCIgXV07IHRoZW5cbiAgICBlY2hvIFwiRG9ja2VyIGNyZWRlbnRpYWxzIGZvdW5kXCJcbiAgICBET0NLRVJfUkVHSVNUUllfVEFSR0VUPVwiJHtEU1RBQ0tfRE9DS0VSX1JFR0lTVFJZOi1kb2NrZXIuaW99XCJcbiAgICBlY2hvIFwiVGFyZ2V0IERvY2tlciByZWdpc3RyeTogJERPQ0tFUl9SRUdJU1RSWV9UQVJHRVRcIlxuXG4gICAgIyBDaGVjayBpZiBhbHJlYWR5IGxvZ2dlZCBpblxuICAgIGlmIGNoZWNrX2RvY2tlcl9sb2dpbiBcIiREU1RBQ0tfRE9DS0VSX1JFR0lTVFJZXCI7IHRoZW5cbiAgICAgICAgZWNobyBcIkFscmVhZHkgbG9nZ2VkIGluIHRvIERvY2tlciByZWdpc3RyeTogJERPQ0tFUl9SRUdJU1RSWV9UQVJHRVRcIlxuICAgIGVsc2VcbiAgICAgICAgZWNobyBcIkxvZ2dpbmcgaW4gdG8gRG9ja2VyIHJlZ2lzdHJ5OiAkRE9DS0VSX1JFR0lTVFJZX1RBUkdFVFwiXG4gICAgICAgICMgTG9naW4gd2l0aG91dCBleHBvc2luZyBwYXNzd29yZCBpbiBwcm9jZXNzIGxpc3RcbiAgICAgICAgaWYgW1sgLW4gXCIkRFNUQUNLX0RPQ0tFUl9SRUdJU1RSWVwiIF1dOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiJERTVEFDS19ET0NLRVJfUEFTU1dPUkRcIiB8IGRvY2tlciBsb2dpbiAtdSBcIiREU1RBQ0tfRE9DS0VSX1VTRVJOQU1FXCIgLS1wYXNzd29yZC1zdGRpbiBcIiREU1RBQ0tfRE9DS0VSX1JFR0lTVFJZXCJcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgZWNobyBcIiREU1RBQ0tfRE9DS0VSX1BBU1NXT1JEXCIgfCBkb2NrZXIgbG9naW4gLXUgXCIkRFNUQUNLX0RPQ0tFUl9VU0VSTkFNRVwiIC0tcGFzc3dvcmQtc3RkaW5cbiAgICAgICAgZmlcblxuICAgICAgICBpZiBbICQ/IC1lcSAwIF07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJEb2NrZXIgbG9naW4gc3VjY2Vzc2Z1bDogJERPQ0tFUl9SRUdJU1RSWV9UQVJHRVRcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiRG9ja2VyIGxvZ2luIGZhaWxlZDogJERPQ0tFUl9SRUdJU1RSWV9UQVJHRVRcIlxuICAgICAgICAgICAgbm90aWZ5X2hvc3RfaG9vdF9lcnJvciBcImRvY2tlciBsb2dpbiBmYWlsZWRcIlxuICAgICAgICAgICAgZXhpdCAxXG4gICAgICAgIGZpXG4gICAgZmlcbiMgQ2hlY2sgaWYgQVdTIEVDUiBjcmVkZW50aWFscyBleGlzdFxuZWxpZiBbWyAtbiBcIiREU1RBQ0tfQVdTX0FDQ0VTU19LRVlfSURcIiAmJiAtbiBcIiREU1RBQ0tfQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZXCIgJiYgLW4gXCIkRFNUQUNLX0FXU19SRUdJT05cIiAmJiAtbiBcIiREU1RBQ0tfQVdTX0VDUl9SRUdJU1RSWVwiIF1dOyB0aGVuXG4gICAgZWNobyBcIkFXUyBFQ1IgY3JlZGVudGlhbHMgZm91bmRcIlxuXG4gICAgIyBDaGVjayBpZiBBV1MgQ0xJIGlzIGluc3RhbGxlZFxuICAgIGlmIFsgISAtZiBcIi4vYXdzL2Rpc3QvYXdzXCIgXTsgdGhlblxuICAgICAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJhd3NjbGkgbm90IGluc3RhbGxlZCwgaW5zdGFsbGluZy4uLlwiXG4gICAgICAgIGVjaG8gXCJBV1MgQ0xJIG5vdCBpbnN0YWxsZWQsIGluc3RhbGxpbmcuLi5cIlxuICAgICAgICBjdXJsIFwiaHR0cHM6Ly9hd3NjbGkuYW1hem9uYXdzLmNvbS9hd3NjbGktZXhlLWxpbnV4LXg4Nl82NC0yLjI0LjE0LnppcFwiIC1vIFwiYXdzY2xpdjIuemlwXCJcbiAgICAgICAgZWNobyBcIjZmZjAzMWEyNmRmN2RhZWJiZmEzY2NkZGM5YWYxNDUwIGF3c2NsaXYyLnppcFwiIHwgbWQ1c3VtIC1jXG4gICAgICAgIGlmIFsgJD8gLW5lIDAgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIk1ENSBjaGVja3N1bSBmYWlsZWRcIlxuICAgICAgICAgICAgbm90aWZ5X2hvc3RfaG9vdF9lcnJvciBcImF3c2NsaSBpbnN0YWxsIGZhaWxlZFwiXG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgZmlcbiAgICAgICAgdW56aXAgYXdzY2xpdjIuemlwICY+IC9kZXYvbnVsbFxuICAgIGVsc2VcbiAgICAgICAgZWNobyBcIkFXUyBDTEkgaXMgYWxyZWFkeSBpbnN0YWxsZWQ6IC4vYXdzL2Rpc3QvYXdzXCJcbiAgICBmaVxuXG4gICAgIyBTZXQgQVdTIGNyZWRlbnRpYWxzIGFzIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICAgIGV4cG9ydCBBV1NfQUNDRVNTX0tFWV9JRD1cIiREU1RBQ0tfQVdTX0FDQ0VTU19LRVlfSURcIlxuICAgIGV4cG9ydCBBV1NfU0VDUkVUX0FDQ0VTU19LRVk9XCIkRFNUQUNLX0FXU19TRUNSRVRfQUNDRVNTX0tFWVwiXG4gICAgZXhwb3J0IEFXU19ERUZBVUxUX1JFR0lPTj1cIiREU1RBQ0tfQVdTX1JFR0lPTlwiXG5cbiAgICAjIFNldCBzZXNzaW9uIHRva2VuIGlmIHByb3ZpZGVkIChmb3IgdGVtcG9yYXJ5IGNyZWRlbnRpYWxzKVxuICAgIGlmIFtbIC1uIFwiJERTVEFDS19BV1NfU0VTU0lPTl9UT0tFTlwiIF1dOyB0aGVuXG4gICAgICAgIGVjaG8gXCJBV1Mgc2Vzc2lvbiB0b2tlbiBmb3VuZCwgdXNpbmcgdGVtcG9yYXJ5IGNyZWRlbnRpYWxzXCJcbiAgICAgICAgZXhwb3J0IEFXU19TRVNTSU9OX1RPS0VOPVwiJERTVEFDS19BV1NfU0VTU0lPTl9UT0tFTlwiXG4gICAgZmlcblxuICAgICMgVGVzdCBBV1MgY3JlZGVudGlhbHMgYmVmb3JlIGF0dGVtcHRpbmcgRUNSIGxvZ2luXG4gICAgZWNobyBcIlRlc3RpbmcgQVdTIGNyZWRlbnRpYWxzLi4uXCJcbiAgICBpZiAhIC4vYXdzL2Rpc3QvYXdzIHN0cyBnZXQtY2FsbGVyLWlkZW50aXR5ICY+IC9kZXYvbnVsbDsgdGhlblxuICAgICAgICBlY2hvIFwiQVdTIGNyZWRlbnRpYWxzIHRlc3QgZmFpbGVkXCJcbiAgICAgICAgIyBGb3Igc2Vzc2lvbiB0b2tlbiBjcmVkZW50aWFscywgdGhpcyBtaWdodCBiZSBleHBlY3RlZCBpZiB0aGV5J3JlIGV4cGlyZWRcbiAgICAgICAgIyBMb2cgd2FybmluZyBidXQgZG9uJ3QgZmFpbCBzdGFydHVwXG4gICAgICAgIGlmIFtbIC1uIFwiJERTVEFDS19BV1NfU0VTU0lPTl9UT0tFTlwiIF1dOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiV2FybmluZzogQVdTIHRlbXBvcmFyeSBjcmVkZW50aWFscyBtYXkgaGF2ZSBleHBpcmVkLCBjb250aW51aW5nIHN0YXJ0dXBcIlxuICAgICAgICAgICAgbm90aWZ5X2hvc3RfaG9vdF9pbmZvIFwiQVdTIHRlbXBvcmFyeSBjcmVkZW50aWFscyBtYXkgaGF2ZSBleHBpcmVkXCJcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgZWNobyBcIkFXUyBjcmVkZW50aWFscyB0ZXN0IGZhaWxlZFwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiSW52YWxpZCBBV1MgY3JlZGVudGlhbHNcIlxuICAgICAgICAgICAgZXhpdCAxXG4gICAgICAgIGZpXG4gICAgZWxzZVxuICAgICAgICBlY2hvIFwiTG9nZ2luZyBpbiB0byBBV1MgRUNSLi4uXCJcbiAgICAgICAgLi9hd3MvZGlzdC9hd3MgZWNyIGdldC1sb2dpbi1wYXNzd29yZCAtLXJlZ2lvbiAkRFNUQUNLX0FXU19SRUdJT04gfCBkb2NrZXIgbG9naW4gLS11c2VybmFtZSBBV1MgLS1wYXNzd29yZC1zdGRpbiBcIiREU1RBQ0tfQVdTX0VDUl9SRUdJU1RSWVwiXG4gICAgICAgIGlmIFsgJD8gLWVxIDAgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIkFXUyBFQ1IgbG9naW4gc3VjY2Vzc2Z1bFwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJBV1MgRUNSIGxvZ2luIHN1Y2Nlc3NmdWxcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiQVdTIEVDUiBsb2dpbiBmYWlsZWRcIlxuICAgICAgICAgICAgIyBGb3Igc2Vzc2lvbiB0b2tlbiBjcmVkZW50aWFscywgZG9uJ3QgZmFpbCBzdGFydHVwIGlmIGxvZ2luIGZhaWxzXG4gICAgICAgICAgICBpZiBbWyAtbiBcIiREU1RBQ0tfQVdTX1NFU1NJT05fVE9LRU5cIiBdXTsgdGhlblxuICAgICAgICAgICAgICAgIGVjaG8gXCJXYXJuaW5nOiBBV1MgRUNSIGxvZ2luIGZhaWxlZCB3aXRoIHRlbXBvcmFyeSBjcmVkZW50aWFscywgY29udGludWluZyBzdGFydHVwXCJcbiAgICAgICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJBV1MgRUNSIGxvZ2luIGZhaWxlZCB3aXRoIHRlbXBvcmFyeSBjcmVkZW50aWFsc1wiXG4gICAgICAgICAgICBlbHNlXG4gICAgICAgICAgICAgICAgbm90aWZ5X2hvc3RfaG9vdF9lcnJvciBcIkFXUyBFQ1IgbG9naW4gZmFpbGVkXCJcbiAgICAgICAgICAgICAgICBleGl0IDFcbiAgICAgICAgICAgIGZpXG4gICAgICAgIGZpXG4gICAgZmlcbmZpXG5cbnBlcmZvcm1fY2xlYW51cFxuXG4jXG4jIEdIQ1IgaW1hZ2UgcHVsbCBhY2Nlc3MgdmVyaWZpY2F0aW9uIChwdXJlIEhUVFAsIG5vIGRvY2tlciBkYWVtb24pXG4jXG5pZiBbWyBcIiRET0NLRVJfUkVHSVNUUllfVEFSR0VUXCIgPT0gXCJnaGNyLmlvXCIgJiYgLW4gXCIkRFNUQUNLX0RPQ0tFUl9VU0VSTkFNRVwiICYmIC1uIFwiJERTVEFDS19ET0NLRVJfUEFTU1dPUkRcIiBdXTsgdGhlblxuICAgIENPTVBPU0VfSU1BR0VTPSQoZ3JlcCAnaW1hZ2U6JyAvZHN0YWNrL2RvY2tlci1jb21wb3NlLnlhbWwgMj4vZGV2L251bGwgfCBhd2sgJ3twcmludCAkMn0nIHwgdHIgLWQgJ1wiJ1wiJ1wiIHx8IHRydWUpXG4gICAgZm9yIGltZyBpbiAkQ09NUE9TRV9JTUFHRVM7IGRvXG4gICAgICAgIFtbIFwiJGltZ1wiICE9IGdoY3IuaW8vKiBdXSAmJiBjb250aW51ZVxuICAgICAgICByZXBvPVwiJHtpbWcjZ2hjci5pby99XCI7IHJlcG89XCIke3JlcG8lJToqfVwiXG4gICAgICAgIHRhZz1cIiR7aW1nIyMqOn1cIjsgW1sgXCIkdGFnXCIgPT0gXCIkaW1nXCIgfHwgXCIkdGFnXCIgPT0gXCIkcmVwb1wiIF1dICYmIHRhZz1cImxhdGVzdFwiXG4gICAgICAgIGVjaG8gXCJWZXJpZnlpbmcgR0hDUiBwdWxsIGFjY2VzczogJGltZ1wiXG4gICAgICAgIHRva2VuPSQoY3VybCAtc2YgLXUgXCIkRFNUQUNLX0RPQ0tFUl9VU0VSTkFNRTokRFNUQUNLX0RPQ0tFUl9QQVNTV09SRFwiICAgICAgICAgICAgIFwiaHR0cHM6Ly9naGNyLmlvL3Rva2VuP3NlcnZpY2U9Z2hjci5pbyZzY29wZT1yZXBvc2l0b3J5OiR7cmVwb306cHVsbFwiIHwganEgLXIgJy50b2tlbiAvLyBlbXB0eScgfHwgdHJ1ZSlcbiAgICAgICAgaWYgW1sgLXogXCIkdG9rZW5cIiBdXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIkVSUk9SOiBHSENSIHRva2VuIGV4Y2hhbmdlIGZhaWxlZCBmb3IgJGltZ1wiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiR0hDUiB0b2tlbiBleGNoYW5nZSBmYWlsZWQ6ICRpbWdcIlxuICAgICAgICAgICAgZXhpdCAxXG4gICAgICAgIGZpXG4gICAgICAgIGh0dHBfY29kZT0kKGN1cmwgLXMgLW8gL2Rldi9udWxsIC13IFwiJXtodHRwX2NvZGV9XCIgLUggXCJBdXRob3JpemF0aW9uOiBCZWFyZXIgJHRva2VuXCIgICAgICAgICAgICAgLUggXCJBY2NlcHQ6IGFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UuaW5kZXgudjEranNvbixhcHBsaWNhdGlvbi92bmQub2NpLmltYWdlLm1hbmlmZXN0LnYxK2pzb24sYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QubGlzdC52Mitqc29uLGFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb25cIiAgICAgICAgICAgICBcImh0dHBzOi8vZ2hjci5pby92Mi8ke3JlcG99L21hbmlmZXN0cy8ke3RhZ31cIilcbiAgICAgICAgaWYgW1sgXCIkaHR0cF9jb2RlXCIgIT0gXCIyMDBcIiBdXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIkVSUk9SOiBHSENSIHB1bGwgYWNjZXNzIGRlbmllZCBmb3IgJGltZyAoSFRUUCAkaHR0cF9jb2RlKVwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiR0hDUiBwdWxsIGFjY2VzcyBkZW5pZWQ6ICRpbWcgKEhUVFAgJGh0dHBfY29kZSlcIlxuICAgICAgICAgICAgZXhpdCAxXG4gICAgICAgIGZpXG4gICAgICAgIGVjaG8gXCJHSENSIHB1bGwgYWNjZXNzIE9LOiAkaW1nXCJcbiAgICBkb25lXG5maVxuXG4jXG4jIFB1bGwgbGF0ZXN0IGltYWdlcyBmcm9tIGRvY2tlci1jb21wb3NlLnlhbWwgc28gZXhpc3RpbmcgQ1ZNcyBwaWNrIHVwIG5ldyB0YWdzLlxuIyBQdWxsIGlzIGZhaWwtc29mdDogc2hvcnQtbGl2ZWQgdG9rZW5zIG1heSBleHBpcmUgYmV0d2VlbiBsb2dpbiBhbmQgcHVsbCwgYnV0XG4jIGZhbGxpbmcgYmFjayB0byBhbHJlYWR5LWNhY2hlZCBpbWFnZXMgaXMgcHJlZmVyYWJsZSB0byBibG9ja2luZyBib290LlxuI1xuZWNobyBcIkltYWdlcyBiZWZvcmUgcHVsbDpcIlxuZG9ja2VyIGltYWdlcyAtLWZvcm1hdCAne3suUmVwb3NpdG9yeX19Ont7LlRhZ319IHt7LklEfX0gKHt7LkNyZWF0ZWRTaW5jZX19KSdcblxuZWNobyBcIlB1bGxpbmcgbGF0ZXN0IGltYWdlcyBmcm9tIC9kc3RhY2svZG9ja2VyLWNvbXBvc2UueWFtbC4uLlwiXG5pZiBkb2NrZXIgY29tcG9zZSAtZiAvZHN0YWNrL2RvY2tlci1jb21wb3NlLnlhbWwgcHVsbDsgdGhlblxuICAgIGVjaG8gXCJkb2NrZXIgY29tcG9zZSBwdWxsIGNvbXBsZXRlZFwiXG4gICAgbm90aWZ5X2hvc3RfaG9vdF9pbmZvIFwiZG9ja2VyIGNvbXBvc2UgcHVsbCBjb21wbGV0ZWRcIlxuZWxzZVxuICAgIGVjaG8gXCJXQVJOSU5HOiBkb2NrZXIgY29tcG9zZSBwdWxsIGZhaWxlZDsgY29udGludWluZyB3aXRoIGV4aXN0aW5nIGltYWdlc1wiXG4gICAgbm90aWZ5X2hvc3RfaG9vdF9pbmZvIFwiZG9ja2VyIGNvbXBvc2UgcHVsbCBmYWlsZWQ7IHVzaW5nIGV4aXN0aW5nIGltYWdlc1wiXG5maVxuXG5lY2hvIFwiSW1hZ2VzIGFmdGVyIHB1bGw6XCJcbmRvY2tlciBpbWFnZXMgLS1mb3JtYXQgJ3t7LlJlcG9zaXRvcnl9fTp7ey5UYWd9fSB7ey5JRH19ICh7ey5DcmVhdGVkU2luY2V9fSknXG5cbiNcbiMgU2V0IHJvb3QgcGFzc3dvcmQuXG4jXG5lY2hvIFwiU2V0dGluZyByb290IHBhc3N3b3JkLi5cIlxuXG4jIENoZWNrIGlmIHBhc3N3b3JkIGZpbGVzIGFyZSB3cml0YWJsZVxuUEFTU1dEX1dSSVRBQkxFPXRydWVcbmlmIFsgISAtdyAvZXRjL3Bhc3N3ZCBdOyB0aGVuXG4gICAgZWNobyBcIldhcm5pbmc6IC9ldGMvcGFzc3dkIGlzIHJlYWQtb25seVwiXG4gICAgUEFTU1dEX1dSSVRBQkxFPWZhbHNlXG5maVxuaWYgWyAhIC13IC9ldGMvc2hhZG93IF07IHRoZW5cbiAgICBlY2hvIFwiV2FybmluZzogL2V0Yy9zaGFkb3cgaXMgcmVhZC1vbmx5XCJcbiAgICBQQVNTV0RfV1JJVEFCTEU9ZmFsc2VcbmZpXG5cbmlmIFsgXCIkUEFTU1dEX1dSSVRBQkxFXCIgPSBcImZhbHNlXCIgXTsgdGhlblxuICAgIGVjaG8gXCJTa2lwcGluZyBwYXNzd29yZCBzZXR1cCBkdWUgdG8gcmVhZC1vbmx5IGZpbGUgc3lzdGVtXCJcbmVsc2VcbiAgICAjIENoZWNrIGlmIGNocGFzc3dkIGlzIGF2YWlsYWJsZVxuICAgIGlmIGNvbW1hbmQgLXYgY2hwYXNzd2QgPi9kZXYvbnVsbCAyPiYxOyB0aGVuXG4gICAgICAgIGVjaG8gXCJVc2luZyBjaHBhc3N3ZCBtZXRob2RcIlxuXG4gICAgICAgIGlmIFsgLW4gXCIkRFNUQUNLX1JPT1RfUEFTU1dPUkRcIiBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiU2V0dGluZyByb290IHBhc3N3b3JkIGZyb20gdXNlci4uXCJcbiAgICAgICAgICAgIGVjaG8gXCJyb290OiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIHwgY2hwYXNzd2RcbiAgICAgICAgICAgIHVuc2V0IERTVEFDS19ST09UX1BBU1NXT1JEXG4gICAgICAgICAgICBlY2hvIFwiUm9vdCBwYXNzd29yZCBzZXQvdXBkYXRlZCBmcm9tIERTVEFDS19ST09UX1BBU1NXT1JEXCJcbiAgICAgICAgZWxpZiBbIC16IFwiJChncmVwICdecm9vdDonIC9ldGMvc2hhZG93IDI+L2Rldi9udWxsIHwgY3V0IC1kOiAtZjIpXCIgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIlNldHRpbmcgcmFuZG9tIHJvb3QgcGFzc3dvcmQuLlwiXG4gICAgICAgICAgICBEU1RBQ0tfUk9PVF9QQVNTV09SRD0kKFxuICAgICAgICAgICAgICAgIExDX0FMTD1DIHRyIC1kYyAnQS1aYS16MC05JyA8IC9kZXYvdXJhbmRvbSB8IGRkIGJzPTEgY291bnQ9MzIgMj4vZGV2L251bGxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGVjaG8gXCJyb290OiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIHwgY2hwYXNzd2RcbiAgICAgICAgICAgIHVuc2V0IERTVEFDS19ST09UX1BBU1NXT1JEXG4gICAgICAgICAgICBlY2hvIFwiUm9vdCBwYXNzd29yZCBzZXQgKHJhbmRvbSBhdXRvLWluaXQpXCJcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgZWNobyBcIlJvb3QgcGFzc3dvcmQgYWxyZWFkeSBzZXQ7IG5vIGNoYW5nZXMuXCJcbiAgICAgICAgZmlcbiAgICBlbHNlXG4gICAgICAgIGVjaG8gXCJVc2luZyBwYXNzd2QgbWV0aG9kXCJcblxuICAgICAgICBpZiBbIC1uIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIlNldHRpbmcgcm9vdCBwYXNzd29yZCBmcm9tIHVzZXIuLlwiXG4gICAgICAgICAgICBlY2hvIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBwYXNzd2QgLS1zdGRpbiByb290IDI+L2Rldi9udWxsICAgICAgICAgICAgICAgICB8fCBwcmludGYgJyVzXG4lc1xuJyBcIiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBwYXNzd2Qgcm9vdFxuICAgICAgICAgICAgdW5zZXQgRFNUQUNLX1JPT1RfUEFTU1dPUkRcbiAgICAgICAgICAgIGVjaG8gXCJSb290IHBhc3N3b3JkIHNldC91cGRhdGVkIGZyb20gRFNUQUNLX1JPT1RfUEFTU1dPUkRcIlxuICAgICAgICBlbGlmIFsgLXogXCIkKGdyZXAgJ15yb290OicgL2V0Yy9zaGFkb3cgMj4vZGV2L251bGwgfCBjdXQgLWQ6IC1mMilcIiBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiU2V0dGluZyByYW5kb20gcm9vdCBwYXNzd29yZC4uXCJcbiAgICAgICAgICAgIERTVEFDS19ST09UX1BBU1NXT1JEPSQoXG4gICAgICAgICAgICAgICAgTENfQUxMPUMgdHIgLWRjICdBLVphLXowLTknIDwgL2Rldi91cmFuZG9tIHwgZGQgYnM9MSBjb3VudD0zMiAyPi9kZXYvbnVsbFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWNobyBcIiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIHwgcGFzc3dkIC0tc3RkaW4gcm9vdCAyPi9kZXYvbnVsbCAgICAgICAgICAgICAgICAgfHwgcHJpbnRmICclc1xuJXNcbicgXCIkRFNUQUNLX1JPT1RfUEFTU1dPUkRcIiBcIiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIHwgcGFzc3dkIHJvb3RcbiAgICAgICAgICAgIHVuc2V0IERTVEFDS19ST09UX1BBU1NXT1JEXG4gICAgICAgICAgICBlY2hvIFwiUm9vdCBwYXNzd29yZCBzZXQgKHJhbmRvbSBhdXRvLWluaXQpXCJcbiAgICAgICAgZWxzZVxuICAgICAgICAgICAgZWNobyBcIlJvb3QgcGFzc3dvcmQgYWxyZWFkeSBzZXQ7IG5vIGNoYW5nZXMuXCJcbiAgICAgICAgZmlcbiAgICBmaVxuZmlcblxuI1xuIyBTZXQgU1NIIGF1dGhvcml6ZWQga2V5c1xuI1xuaWYgbWtkaXIgLXAgL2hvbWUvcm9vdC8uc3NoIDI+L2Rldi9udWxsOyB0aGVuXG4gICAgaWYgW1sgLW4gXCIkRFNUQUNLX1JPT1RfUFVCTElDX0tFWVwiIF1dOyB0aGVuXG4gICAgICAgIGVjaG8gXCIkRFNUQUNLX1JPT1RfUFVCTElDX0tFWVwiID4gL2hvbWUvcm9vdC8uc3NoL2F1dGhvcml6ZWRfa2V5c1xuICAgICAgICB1bnNldCAkRFNUQUNLX1JPT1RfUFVCTElDX0tFWVxuICAgICAgICBlY2hvIFwiUm9vdCBwdWJsaWMga2V5IHNldFwiXG4gICAgZmlcbiAgICBpZiBbWyAtbiBcIiREU1RBQ0tfQVVUSE9SSVpFRF9LRVlTXCIgXV07IHRoZW5cbiAgICAgICAgZWNobyBcIiREU1RBQ0tfQVVUSE9SSVpFRF9LRVlTXCIgPiAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzXG4gICAgICAgIHVuc2V0ICREU1RBQ0tfQVVUSE9SSVpFRF9LRVlTXG4gICAgICAgIGVjaG8gXCJSb290IGF1dGhvcml6ZWRfa2V5cyBzZXRcIlxuICAgIGZpXG5cbiAgICBpZiBbWyAtZiAvZHN0YWNrL3VzZXJfY29uZmlnIF1dICYmIGpxIGVtcHR5IC9kc3RhY2svdXNlcl9jb25maWcgMj4vZGV2L251bGw7IHRoZW5cbiAgICAgICAgaWYgW1sgJChqcSAnaGFzKFwic3NoX2F1dGhvcml6ZWRfa2V5c1wiKScgL2RzdGFjay91c2VyX2NvbmZpZyAyPi9kZXYvbnVsbCkgPT0gXCJ0cnVlXCIgXV07IHRoZW5cbiAgICAgICAgICAgIGpxIC1qICcuc3NoX2F1dGhvcml6ZWRfa2V5cycgL2RzdGFjay91c2VyX2NvbmZpZyA+PiAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzXG4gICAgICAgICAgICAjIFJlbW92ZSBkdXBsaWNhdGVzIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBrZXlzXG4gICAgICAgICAgICBpZiBbWyAkKGNhdCAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzIHwgd2MgLWwpIC1ndCAxIF1dOyB0aGVuXG4gICAgICAgICAgICAgICAgc29ydCAtdSAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzID4gL2hvbWUvcm9vdC8uc3NoL2F1dGhvcml6ZWRfa2V5cy50bXBcbiAgICAgICAgICAgICAgICBtdiAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzLnRtcCAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzXG4gICAgICAgICAgICBmaVxuICAgICAgICAgICAgZWNobyBcIlNldCByb290IGF1dGhvcml6ZWRfa2V5cyBmcm9tIHVzZXIgcHJlZmVyZW5jZXMsIHRvdGFsXCIgJChjYXQgL2hvbWUvcm9vdC8uc3NoL2F1dGhvcml6ZWRfa2V5cyB8IHdjIC1sKSBcImtleXNcIlxuICAgICAgICBmaVxuICAgIGZpXG5lbHNlXG4gICAgZWNobyBcIldhcm5pbmc6IENhbm5vdCBjcmVhdGUgL2hvbWUvcm9vdC8uc3NoIGRpcmVjdG9yeSAocmVhZC1vbmx5IGZpbGUgc3lzdGVtPylcIlxuICAgIGVjaG8gXCJTa2lwcGluZyBTU0gga2V5IHNldHVwXCJcbmZpXG5cbmlmIFtbIC1TIC92YXIvcnVuL2RzdGFjay5zb2NrIF1dOyB0aGVuXG4gICAgZXhwb3J0IERTVEFDS19BUFBfSUQ9JChjdXJsIC1zIC0tdW5peC1zb2NrZXQgL3Zhci9ydW4vZHN0YWNrLnNvY2sgaHR0cDovL2RzdGFjay9JbmZvIHwganEgLWogLmFwcF9pZClcbmVsaWYgW1sgLVMgL3Zhci9ydW4vdGFwcGQuc29jayBdXTsgdGhlblxuICAgIGV4cG9ydCBEU1RBQ0tfQVBQX0lEPSQoY3VybCAtcyAtLXVuaXgtc29ja2V0IC92YXIvcnVuL3RhcHBkLnNvY2sgaHR0cDovL2RzdGFjay9wcnBjL1RhcHBkLkluZm8gfCBqcSAtaiAuYXBwX2lkKVxuZmlcbiMgQ2hlY2sgaWYgRFNUQUNLX0dBVEVXQVlfRE9NQUlOIGlzIG5vdCBzZXQsIHRyeSB0byBnZXQgaXQgZnJvbSB1c2VyX2NvbmZpZyBvciBhcHAtY29tcG9zZS5qc29uXG4jIFByaW9yaXR5OiB1c2VyX2NvbmZpZyA+IGFwcC1jb21wb3NlLmpzb25cbmlmIFtbIC16IFwiJERTVEFDS19HQVRFV0FZX0RPTUFJTlwiIF1dOyB0aGVuXG4gICAgIyBGaXJzdCB0cnkgdG8gZ2V0IGZyb20gL2RzdGFjay91c2VyX2NvbmZpZyBpZiBpdCBleGlzdHMgYW5kIGlzIHZhbGlkIEpTT05cbiAgICBpZiBbWyAtZiAvZHN0YWNrL3VzZXJfY29uZmlnIF1dICYmIGpxIGVtcHR5IC9kc3RhY2svdXNlcl9jb25maWcgMj4vZGV2L251bGw7IHRoZW5cbiAgICAgICAgaWYgW1sgJChqcSAnaGFzKFwiZGVmYXVsdF9nYXRld2F5X2RvbWFpblwiKScgL2RzdGFjay91c2VyX2NvbmZpZyAyPi9kZXYvbnVsbCkgPT0gXCJ0cnVlXCIgXV07IHRoZW5cbiAgICAgICAgICAgIGV4cG9ydCBEU1RBQ0tfR0FURVdBWV9ET01BSU49JChqcSAtaiAnLmRlZmF1bHRfZ2F0ZXdheV9kb21haW4nIC9kc3RhY2svdXNlcl9jb25maWcpXG4gICAgICAgIGZpXG4gICAgZmlcblxuICAgICMgSWYgc3RpbGwgbm90IHNldCwgdHJ5IHRvIGdldCBmcm9tIGFwcC1jb21wb3NlLmpzb25cbiAgICBpZiBbWyAteiBcIiREU1RBQ0tfR0FURVdBWV9ET01BSU5cIiBdXSAmJiBbWyAkKGpxICdoYXMoXCJkZWZhdWx0X2dhdGV3YXlfZG9tYWluXCIpJyBhcHAtY29tcG9zZS5qc29uKSA9PSBcInRydWVcIiBdXTsgdGhlblxuICAgICAgICBleHBvcnQgRFNUQUNLX0dBVEVXQVlfRE9NQUlOPSQoanEgLWogJy5kZWZhdWx0X2dhdGV3YXlfZG9tYWluJyBhcHAtY29tcG9zZS5qc29uKVxuICAgIGZpXG5maVxuaWYgW1sgLW4gXCIkRFNUQUNLX0dBVEVXQVlfRE9NQUlOXCIgXV07IHRoZW5cbiAgICBleHBvcnQgRFNUQUNLX0FQUF9ET01BSU49JERTVEFDS19BUFBfSURcIi5cIiREU1RBQ0tfR0FURVdBWV9ET01BSU5cbmZpXG5cbmVjaG8gXCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXCJcbmVjaG8gXCJTY3JpcHQgZXhlY3V0aW9uIGNvbXBsZXRlZFwiXG5lY2hvIFwiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVwiXG4iLCJwdWJsaWNfbG9ncyI6dHJ1ZSwicHVibGljX3N5c2luZm8iOnRydWUsInB1YmxpY190Y2JpbmZvIjp0cnVlLCJydW5uZXIiOiJkb2NrZXItY29tcG9zZSIsInNlY3VyZV90aW1lIjpmYWxzZSwic3RvcmFnZV9mcyI6InpmcyIsInRwcm94eV9lbmFibGVkIjp0cnVlfQ=="}},"service_capabilities":{"supported_e2ee_versions":["2"],"serving":"aggregator"}},"note":"app_compose is held base64-encoded: the measured compose is a shell script full of paths, and the repository secret scanner reads those as leaked local paths. The test decodes it before hashing."}
\ No newline at end of file
+{"captured_at": "2026-09-14", "endpoint": "https://tee.redpill.ai/v1/aci/attestation", "nonce": "74823bb0e609a4595fe7e6b166e23522d9da5fddec467ede0f1e1c4508de6856", "note": "app_compose is held base64-encoded: the measured compose is a shell script full of paths, and the repository secret scanner reads those as leaked local paths. The test decodes it before hashing.", "report": {"api_version": "aci/1", "workload_keyset_digest": "sha256:9223dbe01170f56efeae9a72b0df74eeb3b30befecb3ec24d9c11413d1a25510", "attestation": {"tee_type": "tdx", "workload_keyset": {"subject": null, "not_after": 1791969683, "receipt_signing_keys": [{"key_id": "dstack-kms-receipt-ed25519-v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a"}], "e2ee_public_keys": [{"key_id": "dstack-kms-e2ee-v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d"}, {"key_id": "dstack-kms-e2ee-x25519-v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537"}], "tls_public_keys": [{"spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b", "domain": "tee.redpill.ai"}, {"spki_sha256": "8bafb8e6ddc55e3e662b6385a2820494fc6e90c64688136860b1851a81b7ee06", "domain": "inference.phala.com"}, {"spki_sha256": "c68585e7dc00a55d3082a9c860cb916e1788c5daec51505e413d27ab94f7d4c7", "domain": "api.redpill.ai"}, {"spki_sha256": "29a17c8e20ffed55893f6d729a75a7cf34637961d268e870842d1f2f7badd7e5", "domain": "openrouter.phala.com"}]}, "report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc2", "source_provenance": {"repo_url": "https://github.com/Dstack-TEE/private-ai-gateway.git", "repo_commit": "3e56bd30dd459d0df90afeb6d63eca7a919bc22f", "image_digest": null, "image_provenance": null}, "evidence": {"quote": "040002008100000000000000939a7233f79c4ca9940a0db3957f06074d79685452a1c5725d6ff7f9d09c91b4000000000b0105000000000000000000000000007bf063280e94fb051f5dd7b1fc59ce9aac42bb961df8d44b709c9b0ff87a7b4df648657ba6d1189589feab1d5a3c9a9d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000702000000000000f06dfda6dce1cf904d4e2bab1dc370634cf95cefa2ceb2de2eee127c9382698090d7a4a13e14c536ec6c9c3c8fa87077011a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046a83f6bbbeaa274aaf04861ef47d7e8898d4e699126b642f72f582e8d7f5349cb266a188bbabf619940d4fece06594b07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878cdf67e467e60edc1737bcf8e682d48131bfb427f523226aa7f197a7608e9b3784783fa759ef5b28191fa12f9ddb36b85819d97a9f6d54660386dddd1892775007e47134c704e91b2654b056fb2174fa5d547fb0d76bf67e75aaafdd641b182e1367ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000cc100000a7a18da0806034774b872a4fcf4dee8ee566b6af0a55f83acbfc76ba5befe2c5108b444149b24e6520ab118a982f953ac52c809f9dce9ee47a8cebbf53fa4916f0bfa28939a048d1d67a664ae6595ec4514829a10a01f7fc10230dd75c44697bf80c2e1e89a20ca66c1f76795d1cf5553a394c311bcc4601b9231b111d70d6b506004610000005050a0a05ff00020000000000000000000000000000000000000000000000000000000000000000000000000000000015000000000000000700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c1dc25ab6fd4ae8afb815c1c46f6d3f9bbe7ab053e699d259bf9136751037b20000000000000000000000000000000000000000000000000000000000000000a83b9d22a44e8d08fb7c247a64a52059f3b6b832995038b1a3b6578b0fc6fd488012210fe4a3762e3019de96ee5eb63bf1977043834cab40034ee81d35efd4082000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538544343424a656741774942416749555359617753595541735374766a483236625541434d506e6f6a797777436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774d7a49784d4463304e5455795768634e4d7a4d774d7a49784d4463304e5455790a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d34394177454841304941424747690a41664533764c54435150696e5470685047695854516c645948654f6f657257516d4b66745141787939446c63353469446b4b537852786e7a38474f7a466f65690a6b584e532b44374575464864666b476a754e796a67674d4e4d4949444354416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464c6d495a7555304e42506c79336e757041556f41396f590a73625a514d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f67594a4b6f5a496876684e415130420a424949434b7a4343416963774867594b4b6f5a496876684e415130424151515162536e396a546e656c37306e456e776e523951666554434341575147436971470a534962345451454e41514977676746554d42414743797147534962345451454e41514942416745464d42414743797147534962345451454e41514943416745460a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745464d42454743797147534962345451454e41514947416749412f7a415142677371686b69472b453042445145434277494241444151426773710a686b69472b4530424451454343414942416a415142677371686b69472b45304244514543435149424144415142677371686b69472b45304244514543436749420a4144415142677371686b69472b45304244514543437749424144415142677371686b69472b45304244514543444149424144415142677371686b69472b4530420a44514543445149424144415142677371686b69472b45304244514543446749424144415142677371686b69472b453042445145434477494241444151426773710a686b69472b45304244514543454149424144415142677371686b69472b45304244514543455149424454416642677371686b69472b45304244514543456751510a425155434167582f4141494141414141414141414144415142676f71686b69472b45304244514544424149414144415542676f71686b69472b453042445145450a424159676f473841414141774477594b4b6f5a496876684e4151304242516f424154416542676f71686b69472b453042445145474242414e78727a30753075620a48784e354e3964353631576d4d45514743697147534962345451454e415163774e6a415142677371686b69472b45304244514548415145422f7a4151426773710a686b69472b45304244514548416745422f7a415142677371686b69472b45304244514548417745422f7a414b42676771686b6a4f5051514441674e49414442460a4169454131305850553751764338564a743949654f724a366c4430744b58462b54746a4e422f6f7a62635235517477434944576e57773946484e7351745359420a70394c5463792f5670376f517367417076763332732f4236576b73760a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "quote_report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000", "event_log": "[{\"imr\":0,\"event_type\":2147483659,\"digest\":\"5fe4f9ee465a361d077a6892f6bc4d19a82b272fea2e3c2c1ef9c33b4d9050115fca0c6dc7a7f68f91c623de38c48226\",\"event\":\"\",\"event_payload\":\"095464785461626c65000100000000000000af96bb93f2b9b84e9462e0ba745642360090800000000000\"},{\"imr\":0,\"event_type\":2147483658,\"digest\":\"344bc51c980ba621aaa00da3ed7436f7d6e549197dfe699515dfa2c6583d95e6412af21c097d473155875ffd561d6790\",\"event\":\"\",\"event_payload\":\"2946762858585858585858582d585858582d585858582d585858582d58585858585858585858585829000000c0ff000000000040080000000000\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"9dc3a1f80bcec915391dcda5ffbb15e7419f77eab462bbf72b42166fb70d50325e37b36f93537a863769bcf9bedae6fb\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0a00000000000000000000000000000053006500630075007200650042006f006f007400\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"6f2e3cbc14f9def86980f5f66fd85e99d63e69a73014ed8a5633ce56eca5b64b692108c56110e22acadcef58c3250f1b\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0200000000000000000000000000000050004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"d607c0efb41c0d757d69bca0615c3a9ac0b1db06c557d992e906c6b7dee40e0e031640c7bfd7bcd35844ef9edeadc6f9\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c030000000000000000000000000000004b0045004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"08a74f8963b337acb6c93682f934496373679dd26af1089cb4eaf0c30cf260a12e814856385ab8843e56a9acea19e127\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f0200000000000000000000000000000064006200\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"18cc6e01f0c6ea99aa23f8a280423e94ad81d96d0aeb5180504fc0f7a40cb3619dd39bd6a95ec1680a86ed6ab0f9828d\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f03000000000000000000000000000000640062007800\"},{\"imr\":0,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":0,\"event_type\":10,\"digest\":\"febfba94a148ef74278a30ed690856a468636b51f619d44025c4f9dc0212068369895d35c67157ccd2149cd049f3761c\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"5427470ec5de45a3ad085101e7567c7f0b4aa4b0147d9aa583f067fdcb15155fc52a22bd23fd8ea10d045751f8e52337\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"2194a807023a3ce948e26538252c046bce7c76293a3750ac22a41d1c2a3584bd53cc95bbdbde2d0c9f3a906f2663840f\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":1,\"event_type\":2147483651,\"digest\":\"90e278aa4d688f4f05bf76f1786fea174abcea0d6b44e81cea5b3327f3f211e5c465a9c34b513283bed2512bca6d4a19\",\"event\":\"\",\"event_payload\":\"1860fd7a0000000000c485000000000000000000000000002a000000000000000403140072f728144ab61e44b8c39ebdd7f893c7040412006b00650072006e0065006c0000007fff0400\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0900000000000000020000000000000042006f006f0074004f0072006400650072000000\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"23ada07f5261f12f34a0bd8e46760962d6b4d576a416f1fea1c64bc656b1d28eacf7047ae6e967c58fd2a98bfa74c298\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c08000000000000003e0000000000000042006f006f0074003000300030003000090100002c0055006900410070007000000004071400c9bdb87cebf8344faaea3ee4af6516a10406140021aa2c4614760345836e8ab6f46623317fff0400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71\",\"event\":\"\",\"event_payload\":\"43616c6c696e6720454649204170706c69636174696f6e2066726f6d20426f6f74204f7074696f6e\"},{\"imr\":1,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":2,\"event_type\":6,\"digest\":\"9c824b958dbbef07ec732fd79727e995d5eca309dbc6d62e4fd2840f267c5e87108b5fe25c7997c67801ff391fb67ffc\",\"event\":\"\",\"event_payload\":\"ed223b8f1a0000004c4f414445445f494d4147453a3a4c6f61644f7074696f6e7300\"},{\"imr\":2,\"event_type\":6,\"digest\":\"5eb4f34f2ec49071551c5562ebaab9f86408f4a0eadaa3d7f05157e37ab04a1f040a82eaae94015d628a7ea4aa482cd2\",\"event\":\"\",\"event_payload\":\"ec223b8f0d0000004c696e757820696e6974726400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0\",\"event\":\"\",\"event_payload\":\"4578697420426f6f7420536572766963657320496e766f636174696f6e\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74\",\"event\":\"\",\"event_payload\":\"4578697420426f6f742053657276696365732052657475726e656420776974682053756363657373\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"f9974020ef507068183313d0ca808e0d1ca9b2d1ad0c61f5784e7157c362c06536f5ddacdad4451693f48fcc72fff624\",\"event\":\"system-preparing\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"8562fc3980258ea5119a31356844547c42ba696d73f6ecdc7d29732beef0083f22674ce14349a8a1400254466e851407\",\"event\":\"app-id\",\"event_payload\":\"fdb7a14e5a6675f752e2cb69c9067a98ca402918\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"5edb4d5fbf301c47c77faa3de6edbcb97b93521b7c63ac6a8d8028d2f1891bd8517d76285f6e724cf28451acaac6c89e\",\"event\":\"compose-hash\",\"event_payload\":\"1a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"dbb7d7c2c2ce19938fb5989949f2c4bba6829ced5cd7b8c0d98696c293ac1c5dc8b1bfb9bc95b61e2ff778a3686651eb\",\"event\":\"instance-id\",\"event_payload\":\"15e847e02a22706121de347498427469d2e7c379\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"98bd7e6bd3952720b65027fd494834045d06b4a714bf737a06b874638b3ea00ff402f7f583e3e3b05e921c8570433ac6\",\"event\":\"boot-mr-done\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"65828c6f922910fd0e272501a8b2bb6b03e24c99bb19d1cd530586061d8738f9dd0564752db23d556a6b381cbf465a85\",\"event\":\"mr-kms\",\"event_payload\":\"692afc6d7a86a32cfc1ebd9cad1a576aab012bab46986ba609bc8d6407270572\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"c936f709860155a455c519b505fe29bde21d1b778534a8f0b41b706b0f789eae2ea899e224fedb2331c18324aab4db93\",\"event\":\"os-image-hash\",\"event_payload\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"83368b43a0fc6f824f5a9220592df85fd30e2d405ecbd253a5c6354af63e6c9b41aec557c38a38e348ab87f9ac8fc68c\",\"event\":\"key-provider\",\"event_payload\":\"7b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343838343465623432636364663863353266643466313734663336326663623962626431396334356664343866316564656332643866316361323335333665633161373430323162346365653631306330373466383239346434333162326237666565326333396535333333666461663061343532326434336662313539643966227d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"ba51104636900268b0e059fa3d266419d079d1e94aea26fb9fcbb8d764bf4c89a67ac271b8a0d1a3989945132a111fc7\",\"event\":\"storage-fs\",\"event_payload\":\"7a6673\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"1a76b2a80a0be71eae59f80945d876351a7a3fb8e9fd1ff1cede5734aa84ea11fd72b4edfbb6f04e5a85edd114c751bd\",\"event\":\"system-ready\",\"event_payload\":\"\"}]", "vm_config": "{\"os_image_hash\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\",\"cpu_count\":32,\"memory_size\":34359738368,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.5.9-bd369a8c\",\"host_share_mode\":\"9p\",\"spec_version\":1}", "app_compose": "{\"allowed_envs\":[\"PRIVATE_AI_GATEWAY_ADMIN_TOKEN\",\"PRIVATE_AI_GATEWAY_CONTROL_URL\",\"PRIVATE_AI_GATEWAY_CONTROL_TOKEN\",\"DSTACK_ROOT_PUBLIC_KEY\",\"CLOUDFLARE_API_TOKEN\",\"CERTBOT_EMAIL\",\"NODE_EXPORTER_USER\",\"NODE_EXPORTER_PASSWORD_HASH\",\"PRIVATE_AI_GATEWAY_PULL_TOKEN\"],\"docker_compose_file\":\"# dstack / Phala deployment manifest for the private-ai-gateway stack via the launcher.\\n#\\n# The launcher image is the measured trust-root: pin it by @sha256 digest (the\\n# digest + this file are hashed into the TDX quote). The launcher clones+builds\\n# the gateway from source at boot; the attestation verifier is\\n# the gateway's vendored confidential_verifier package, so it needs no separate pin.\\n#\\n# The gateway's source pin + static config are inlined as compose `configs:`\\n# (measured into the attestation): the launcher clones the pin and the gateway\\n# reads the config from mounted files. Secret values are supplied as dstack\\n# ENCRYPTED SECRETS and referenced as ${VAR} so dstack injects them \u2014 only the\\n# variable names appear in this public file, never the values.\\n\\nservices:\\n # TLS-terminating ingress: requests CAA-pinned Let's Encrypt certs via Cloudflare\\n # DNS-01, registers under the dstack gateway domain, and reverse-proxies inbound\\n # 443 to the launcher's gateway on :8086. Pin by @sha256 digest like the launcher.\\n dstack-ingress:\\n image: dstacktee/dstack-ingress:2.2@sha256:d05a7b343c37c1cca1bba8dbf7e8f3c6d2118158af2d41c455103796db4f67f0\\n restart: unless-stopped\\n # The ingress is an L4 TCP proxy: every client connection maps 1:1 to a\\n # backend connection to the gateway, so it holds 2 fds per live connection.\\n # haproxy has no `ulimit-n` in its config; it derives an fd need from MAXCONN\\n # and raises its OWN soft nofile from the container hard limit. Pin the hard\\n # limit here (like `launcher`) so MAXCONN is never silently capped by whatever\\n # the docker daemon default happens to be. 65536 comfortably covers\\n # MAXCONN 16384 (~33k fds) with headroom.\\n ulimits:\\n nofile:\\n soft: 65536\\n hard: 65536\\n ports:\\n - \\\"443:443\\\"\\n environment:\\n # --- public config: literals, measured into the attestation ---\\n DNS_PROVIDER: cloudflare\\n GATEWAY_DOMAIN: _.${DSTACK_GATEWAY_DOMAIN}\\n SET_CAA: \\\"true\\\"\\n # Concurrent-connection ceiling. The image default is 4096; under real\\n # streaming load the ingress sat pinned at that ceiling ~84% of the time\\n # (avg ~3720 concurrent), so once a burst pushed past 4096 haproxy stopped\\n # accepting and clients saw a hang. Streaming LLM traffic keeps thousands of\\n # long-lived connections open at once, so give a 4x headroom. Must stay well\\n # under the nofile hard limit above (2 fds/conn in TCP mode).\\n MAXCONN: \\\"16384\\\"\\n # Idle (no-data) timeout, reset on any byte in either direction. The image\\n # default is 86400s (24h): a client that stops reading, or an upstream that\\n # goes silent, keeps its slot for a full day, which is how slots leaked\\n # toward the ceiling. 1800s reaps a genuinely idle/stalled connection while\\n # never touching a stream that is still flowing (even a slow one). NOTE:\\n # this is the one value to confirm against the longest legitimate silent\\n # gap a request can have \u2014 e.g. a reasoning model that thinks >30 min before\\n # its first token would be cut. Raise if such requests are expected.\\n TIMEOUT_CLIENT: 1800s\\n TIMEOUT_SERVER: 1800s\\n # Multi-domain TLS. The ingress is an L4 (TCP) proxy: it terminates TLS for\\n # each hostname and forwards the DECRYPTED stream untouched, so the client's\\n # original HTTP Host survives. The gateway uses that Host to pick the\\n # matching downstream_tls_binding. All public hostnames route by SNI to the\\n # same gateway listener (compose service name, not localhost). tee.redpill.ai\\n # and inference.phala.com are the TEE-only domains (see `tee_only_domains`\\n # below); api.redpill.ai and openrouter.phala.com are the non-TEE domains.\\n DOMAINS: |\\n tee.redpill.ai\\n inference.phala.com\\n api.redpill.ai\\n openrouter.phala.com\\n ROUTING_MAP: |\\n tee.redpill.ai=launcher:8086\\n inference.phala.com=launcher:8086\\n api.redpill.ai=launcher:8086\\n openrouter.phala.com=launcher:8086\\n # --- secrets: values come from dstack Encrypted Secrets (never commit values) ---\\n # NOTE: this single Cloudflare token must have DNS-edit (and CAA) rights on\\n # BOTH the redpill.ai and phala.com zones, or cert issuance for one fails.\\n CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}\\n CERTBOT_EMAIL: ${CERTBOT_EMAIL}\\n volumes:\\n - /var/run/dstack.sock:/var/run/dstack.sock\\n - /var/run/tappd.sock:/var/run/tappd.sock\\n - cert-data:/etc/letsencrypt\\n # Per-domain leaf certs (cert-.pem) the gateway reads to bind SPKI.\\n - evidences:/evidences\\n\\n launcher:\\n image: ghcr.io/redpill-ai/private-ai-launcher@sha256:c083ff9e6a5ddf10f6c9e9bb1f74cc618deebecfea5208b563c574399db4637c\\n restart: always\\n # The gateway opens many fds (inbound conns, per-upstream pools, control-plane\\n # + verifier calls). Docker's default nofile (often 1024) is far too low: under\\n # load it hit EMFILE (\\\"Too many open files\\\"), which killed the gateway's accept\\n # loop. Raise the limit well above peak concurrency.\\n ulimits:\\n nofile:\\n soft: 65536\\n hard: 65536\\n # supervisord streams the gateway logs to the container stdout; cap the\\n # runtime log on disk so it can't grow unbounded (~5GB total, rotated).\\n # Sized for the gateway's always-on request_outcome failure logging:\\n # roughly a month of retention on the persistent volume for incident\\n # forensics.\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"500m\\\"\\n max-file: \\\"10\\\"\\n # no host port: traffic enters via dstack-ingress (443) and reaches the\\n # gateway over the compose network as http://launcher:8086.\\n expose:\\n - \\\"8086\\\"\\n volumes:\\n - /var/run/dstack.sock:/var/run/dstack.sock # only the gateway uses this\\n - pal-cache:/var/lib/pag/cache # cargo/uv build cache (speeds reboots)\\n - pal-state:/var/lib/pag/state # mutable upstreams.json + sessions.jsonl\\n - evidences:/evidences:ro # ingress-minted per-domain leaf certs (cert-.pem)\\n # Static gateway config + upstream seed are mounted from the `configs:` block\\n # at the bottom (measured into the attestation). The source pin is passed via\\n # `environment:` instead (see below).\\n configs:\\n - source: gateway-config\\n target: /etc/private-ai-gateway/gateway.config.json\\n - source: gateway-upstreams\\n target: /etc/private-ai-gateway/upstreams.seed.json\\n environment:\\n # The gateway reads its whole policy from this mounted static config file\\n # (see `configs:` above); CONFIG_PATH is the only gateway-facing env var.\\n - PRIVATE_AI_GATEWAY_CONFIG_PATH=/etc/private-ai-gateway/gateway.config.json\\n # Tracing filter. `request_outcome=debug` turns on the raw error-detail\\n # field of the per-request failure log \u2014 a deliberate operator choice:\\n # upstream error bodies may echo request fragments into these\\n # operator-visible logs. Drop the `request_outcome=debug` part to fall\\n # back to content-free failure logging only.\\n - RUST_LOG=info,request_outcome=debug\\n # Gateway source pin. These literals are measured into the attestation, and\\n # because they live in `environment:` (part of Compose's config-hash) a\\n # COMMIT_SHA bump forces a container recreate on `phala deploy` \u2014 so the new\\n # pin actually takes effect. (A `configs:`-file pin would not: Compose ignores\\n # config content when deciding to recreate, so the change would never apply.)\\n # entrypoint.sh writes /etc/git-launcher/gateway.conf from these, and the\\n # gateway reads that file as its attested source provenance.\\n - PRIVATE_AI_GATEWAY_REPO_URL=https://github.com/Dstack-TEE/private-ai-gateway.git\\n - PRIVATE_AI_GATEWAY_REPO_COMMIT=3e56bd30dd459d0df90afeb6d63eca7a919bc22f\\n # NEAR AI verification posts TDX quotes to the dstack-verifier sidecar below;\\n # reach it by compose service name (localhost would be the launcher itself).\\n - DSTACK_VERIFIER_URL=http://dstack-verifier:8080\\n # --- secrets: values come from dstack Encrypted Secrets (never commit values) ---\\n # Control-plane credentials. The gateway's in-process middleware reads these\\n # from the `middleware` config section below (rendered from the same secrets).\\n - PRIVATE_AI_GATEWAY_CONTROL_URL=${PRIVATE_AI_GATEWAY_CONTROL_URL}\\n - PRIVATE_AI_GATEWAY_CONTROL_TOKEN=${PRIVATE_AI_GATEWAY_CONTROL_TOKEN}\\n\\n # TDX quote verifier for the NEAR AI path (reproduces ACPI/boot measurements via\\n # QEMU). The gateway reaches it at http://dstack-verifier:8080 (DSTACK_VERIFIER_URL).\\n dstack-verifier:\\n image: dstacktee/dstack-verifier:0.6.0-rc3@sha256:0ed61d60f6e0d44cf466e15dc6894f37152d8567cfa76bd8785a921b2d906c38\\n restart: unless-stopped\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"50m\\\"\\n max-file: \\\"5\\\"\\n\\n # Host-metrics exporter for observing this CVM (CPU/mem/disk/net/fd). Inside a\\n # CVM the container's /, /proc, /sys ARE the guest's, so node-exporter reports\\n # the CVM's own state. Pinned by @sha256 like the other images (measured into\\n # the attestation). Exposed on 9100 (reached via the dstack gateway) and guarded\\n # by HTTP basic auth \u2014 both username and the bcrypt password HASH are injected as\\n # dstack Encrypted Secrets, so neither appears in this measured file.\\n node-exporter:\\n image: prom/node-exporter:v1.8.2@sha256:4032c6d5bfd752342c3e631c2f1de93ba6b86c41db6b167b9a35372c139e7706\\n restart: unless-stopped\\n pid: host\\n command:\\n - '--path.rootfs=/host'\\n - '--path.procfs=/host/proc'\\n - '--path.sysfs=/host/sys'\\n - '--web.config.file=/etc/node-exporter/web-config.yml'\\n volumes:\\n - /:/host:ro,rslave\\n configs:\\n - source: node-exporter-web-config\\n target: /etc/node-exporter/web-config.yml\\n ports:\\n - \\\"9100:9100\\\"\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"10m\\\"\\n max-file: \\\"3\\\"\\n\\nvolumes:\\n pal-cache:\\n pal-state:\\n cert-data: # Let's Encrypt certs/keys persisted for dstack-ingress\\n evidences: # ingress attestation evidence + per-domain leaf certs, shared read-only with the gateway\\n\\n# Inlined, measured into the attestation. Public pins use LITERAL values so the\\n# code version is bound into compose_hash; only secrets use ${VAR} (injected by\\n# dstack, never measured).\\nconfigs:\\n # Static gateway config. `admin_token`, the dedicated upstream pull token, and\\n # the control-plane credentials are secrets \u2014 ${VAR} keeps their values out of\\n # the measured compose while the presence and destination stay measured. The\\n # `middleware` section runs the in-process middleware, which consults the control\\n # plane at `control_url`.\\n gateway-config:\\n content: |\\n {\\n \\\"bind\\\": \\\"0.0.0.0:8086\\\",\\n \\\"state_dir\\\": \\\"/var/lib/pag/state\\\",\\n \\\"upstream_config_seed_path\\\": \\\"/etc/private-ai-gateway/upstreams.seed.json\\\",\\n \\\"upstream_pull\\\": {\\n \\\"url\\\": \\\"https://service.redpill.ai/api/admin/gateway-upstreams/config\\\",\\n \\\"token\\\": \\\"${PRIVATE_AI_GATEWAY_PULL_TOKEN:?set PRIVATE_AI_GATEWAY_PULL_TOKEN}\\\",\\n \\\"refresh_seconds\\\": 300,\\n \\\"request_timeout_seconds\\\": 90\\n },\\n \\\"admin_token\\\": \\\"${PRIVATE_AI_GATEWAY_ADMIN_TOKEN:?set PRIVATE_AI_GATEWAY_ADMIN_TOKEN}\\\",\\n \\\"tls\\\": {\\n \\\"domain_certificates\\\": [\\n { \\\"domain\\\": \\\"tee.redpill.ai\\\", \\\"certificate_path\\\": \\\"/evidences/cert-tee.redpill.ai.pem\\\" },\\n { \\\"domain\\\": \\\"inference.phala.com\\\", \\\"certificate_path\\\": \\\"/evidences/cert-inference.phala.com.pem\\\" },\\n { \\\"domain\\\": \\\"api.redpill.ai\\\", \\\"certificate_path\\\": \\\"/evidences/cert-api.redpill.ai.pem\\\" },\\n { \\\"domain\\\": \\\"openrouter.phala.com\\\", \\\"certificate_path\\\": \\\"/evidences/cert-openrouter.phala.com.pem\\\" }\\n ]\\n },\\n \\\"middleware\\\": {\\n \\\"control_url\\\": \\\"${PRIVATE_AI_GATEWAY_CONTROL_URL:?set PRIVATE_AI_GATEWAY_CONTROL_URL}\\\",\\n \\\"control_token\\\": \\\"${PRIVATE_AI_GATEWAY_CONTROL_TOKEN:?set PRIVATE_AI_GATEWAY_CONTROL_TOKEN}\\\",\\n \\\"tee_only_domains\\\": [\\\"tee.redpill.ai\\\", \\\"inference.phala.com\\\"]\\n }\\n }\\n # Initial upstream routes: seeded into /upstreams.json only when empty,\\n # then managed at runtime via PUT /v1/admin/upstreams (does not touch this file).\\n gateway-upstreams:\\n content: |\\n []\\n # node-exporter basic-auth credentials. exporter-toolkit's basic_auth_users maps\\n # username -> bcrypt hash; both are ${VAR} so dstack substitutes the injected\\n # secret values when rendering this file into the container \u2014 only the literals\\n # are measured. NODE_EXPORTER_PASSWORD_HASH must be a bcrypt hash\\n # (`htpasswd -nBC 10 \\\"\\\" | tr -d ':\\\\n'`), NOT the plaintext password; the plaintext\\n # goes to the external Prometheus scrape config.\\n node-exporter-web-config:\\n content: |\\n basic_auth_users:\\n ${NODE_EXPORTER_USER:?set NODE_EXPORTER_USER}: ${NODE_EXPORTER_PASSWORD_HASH:?set NODE_EXPORTER_PASSWORD_HASH}\\n\",\"features\":[\"kms\",\"tproxy-net\"],\"gateway_enabled\":true,\"kms_enabled\":true,\"local_key_provider_enabled\":false,\"manifest_version\":2,\"name\":\"\",\"no_instance_id\":false,\"pre_launch_script\":\"#!/bin/bash\\necho \\\"----------------------------------------------\\\"\\necho \\\"Running Phala Cloud Pre-Launch Script v0.0.15\\\"\\necho \\\"----------------------------------------------\\\"\\nset -e\\n\\n# Function: notify host\\n\\nnotify_host() {\\n if command -v dstack-util >/dev/null 2>&1; then\\n dstack-util notify-host -e \\\"$1\\\" -d \\\"$2\\\"\\n else\\n tdxctl notify-host -e \\\"$1\\\" -d \\\"$2\\\"\\n fi\\n}\\n\\nnotify_host_hoot_info() {\\n notify_host \\\"boot.progress\\\" \\\"$1\\\"\\n}\\n\\nnotify_host_hoot_error() {\\n notify_host \\\"boot.error\\\" \\\"$1\\\"\\n}\\n\\n# Function: Perform Docker cleanup\\nperform_cleanup() {\\n echo \\\"Pruning unused images\\\"\\n docker image prune -af\\n echo \\\"Pruning unused volumes\\\"\\n docker volume prune -f\\n notify_host_hoot_info \\\"docker cleanup completed\\\"\\n}\\n\\n# Function: Check Docker login status without exposing credentials\\ncheck_docker_login() {\\n local registry=\\\"$1\\\"\\n\\n # When registry is specified, check auth entry for that registry in Docker config\\n if [[ -n \\\"$registry\\\" ]]; then\\n local docker_config_path=\\\"${DOCKER_CONFIG:-$HOME/.docker}/config.json\\\"\\n if [[ -f \\\"$docker_config_path\\\" ]] && grep -q \\\"$registry\\\" \\\"$docker_config_path\\\"; then\\n return 0\\n else\\n return 1\\n fi\\n fi\\n\\n # Fallback check when no explicit registry is provided\\n if docker info 2>/dev/null | grep -q \\\"Username\\\"; then\\n return 0\\n else\\n return 1\\n fi\\n}\\n\\n# Main logic starts here\\necho \\\"Starting login process...\\\"\\n\\n# Check if Docker credentials exist\\nif [[ -n \\\"$DSTACK_DOCKER_USERNAME\\\" && -n \\\"$DSTACK_DOCKER_PASSWORD\\\" ]]; then\\n echo \\\"Docker credentials found\\\"\\n DOCKER_REGISTRY_TARGET=\\\"${DSTACK_DOCKER_REGISTRY:-docker.io}\\\"\\n echo \\\"Target Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n\\n # Check if already logged in\\n if check_docker_login \\\"$DSTACK_DOCKER_REGISTRY\\\"; then\\n echo \\\"Already logged in to Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n else\\n echo \\\"Logging in to Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n # Login without exposing password in process list\\n if [[ -n \\\"$DSTACK_DOCKER_REGISTRY\\\" ]]; then\\n echo \\\"$DSTACK_DOCKER_PASSWORD\\\" | docker login -u \\\"$DSTACK_DOCKER_USERNAME\\\" --password-stdin \\\"$DSTACK_DOCKER_REGISTRY\\\"\\n else\\n echo \\\"$DSTACK_DOCKER_PASSWORD\\\" | docker login -u \\\"$DSTACK_DOCKER_USERNAME\\\" --password-stdin\\n fi\\n\\n if [ $? -eq 0 ]; then\\n echo \\\"Docker login successful: $DOCKER_REGISTRY_TARGET\\\"\\n else\\n echo \\\"Docker login failed: $DOCKER_REGISTRY_TARGET\\\"\\n notify_host_hoot_error \\\"docker login failed\\\"\\n exit 1\\n fi\\n fi\\n# Check if AWS ECR credentials exist\\nelif [[ -n \\\"$DSTACK_AWS_ACCESS_KEY_ID\\\" && -n \\\"$DSTACK_AWS_SECRET_ACCESS_KEY\\\" && -n \\\"$DSTACK_AWS_REGION\\\" && -n \\\"$DSTACK_AWS_ECR_REGISTRY\\\" ]]; then\\n echo \\\"AWS ECR credentials found\\\"\\n\\n # Check if AWS CLI is installed\\n if [ ! -f \\\"./aws/dist/aws\\\" ]; then\\n notify_host_hoot_info \\\"awscli not installed, installing...\\\"\\n echo \\\"AWS CLI not installed, installing...\\\"\\n curl \\\"https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.24.14.zip\\\" -o \\\"awscliv2.zip\\\"\\n echo \\\"6ff031a26df7daebbfa3ccddc9af1450 awscliv2.zip\\\" | md5sum -c\\n if [ $? -ne 0 ]; then\\n echo \\\"MD5 checksum failed\\\"\\n notify_host_hoot_error \\\"awscli install failed\\\"\\n exit 1\\n fi\\n unzip awscliv2.zip &> /dev/null\\n else\\n echo \\\"AWS CLI is already installed: ./aws/dist/aws\\\"\\n fi\\n\\n # Set AWS credentials as environment variables\\n export AWS_ACCESS_KEY_ID=\\\"$DSTACK_AWS_ACCESS_KEY_ID\\\"\\n export AWS_SECRET_ACCESS_KEY=\\\"$DSTACK_AWS_SECRET_ACCESS_KEY\\\"\\n export AWS_DEFAULT_REGION=\\\"$DSTACK_AWS_REGION\\\"\\n\\n # Set session token if provided (for temporary credentials)\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"AWS session token found, using temporary credentials\\\"\\n export AWS_SESSION_TOKEN=\\\"$DSTACK_AWS_SESSION_TOKEN\\\"\\n fi\\n\\n # Test AWS credentials before attempting ECR login\\n echo \\\"Testing AWS credentials...\\\"\\n if ! ./aws/dist/aws sts get-caller-identity &> /dev/null; then\\n echo \\\"AWS credentials test failed\\\"\\n # For session token credentials, this might be expected if they're expired\\n # Log warning but don't fail startup\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"Warning: AWS temporary credentials may have expired, continuing startup\\\"\\n notify_host_hoot_info \\\"AWS temporary credentials may have expired\\\"\\n else\\n echo \\\"AWS credentials test failed\\\"\\n notify_host_hoot_error \\\"Invalid AWS credentials\\\"\\n exit 1\\n fi\\n else\\n echo \\\"Logging in to AWS ECR...\\\"\\n ./aws/dist/aws ecr get-login-password --region $DSTACK_AWS_REGION | docker login --username AWS --password-stdin \\\"$DSTACK_AWS_ECR_REGISTRY\\\"\\n if [ $? -eq 0 ]; then\\n echo \\\"AWS ECR login successful\\\"\\n notify_host_hoot_info \\\"AWS ECR login successful\\\"\\n else\\n echo \\\"AWS ECR login failed\\\"\\n # For session token credentials, don't fail startup if login fails\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"Warning: AWS ECR login failed with temporary credentials, continuing startup\\\"\\n notify_host_hoot_info \\\"AWS ECR login failed with temporary credentials\\\"\\n else\\n notify_host_hoot_error \\\"AWS ECR login failed\\\"\\n exit 1\\n fi\\n fi\\n fi\\nfi\\n\\nperform_cleanup\\n\\n#\\n# GHCR image pull access verification (pure HTTP, no docker daemon)\\n#\\nif [[ \\\"$DOCKER_REGISTRY_TARGET\\\" == \\\"ghcr.io\\\" && -n \\\"$DSTACK_DOCKER_USERNAME\\\" && -n \\\"$DSTACK_DOCKER_PASSWORD\\\" ]]; then\\n COMPOSE_IMAGES=$(grep 'image:' /dstack/docker-compose.yaml 2>/dev/null | awk '{print $2}' | tr -d '\\\"'\\\"'\\\" || true)\\n for img in $COMPOSE_IMAGES; do\\n [[ \\\"$img\\\" != ghcr.io/* ]] && continue\\n repo=\\\"${img#ghcr.io/}\\\"; repo=\\\"${repo%%:*}\\\"\\n tag=\\\"${img##*:}\\\"; [[ \\\"$tag\\\" == \\\"$img\\\" || \\\"$tag\\\" == \\\"$repo\\\" ]] && tag=\\\"latest\\\"\\n echo \\\"Verifying GHCR pull access: $img\\\"\\n token=$(curl -sf -u \\\"$DSTACK_DOCKER_USERNAME:$DSTACK_DOCKER_PASSWORD\\\" \\\"https://ghcr.io/token?service=ghcr.io&scope=repository:${repo}:pull\\\" | jq -r '.token // empty' || true)\\n if [[ -z \\\"$token\\\" ]]; then\\n echo \\\"ERROR: GHCR token exchange failed for $img\\\"\\n notify_host_hoot_error \\\"GHCR token exchange failed: $img\\\"\\n exit 1\\n fi\\n http_code=$(curl -s -o /dev/null -w \\\"%{http_code}\\\" -H \\\"Authorization: Bearer $token\\\" -H \\\"Accept: application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json\\\" \\\"https://ghcr.io/v2/${repo}/manifests/${tag}\\\")\\n if [[ \\\"$http_code\\\" != \\\"200\\\" ]]; then\\n echo \\\"ERROR: GHCR pull access denied for $img (HTTP $http_code)\\\"\\n notify_host_hoot_error \\\"GHCR pull access denied: $img (HTTP $http_code)\\\"\\n exit 1\\n fi\\n echo \\\"GHCR pull access OK: $img\\\"\\n done\\nfi\\n\\n#\\n# Pull latest images from docker-compose.yaml so existing CVMs pick up new tags.\\n# Pull is fail-soft: short-lived tokens may expire between login and pull, but\\n# falling back to already-cached images is preferable to blocking boot.\\n#\\necho \\\"Images before pull:\\\"\\ndocker images --format '{{.Repository}}:{{.Tag}} {{.ID}} ({{.CreatedSince}})'\\n\\necho \\\"Pulling latest images from /dstack/docker-compose.yaml...\\\"\\nif docker compose -f /dstack/docker-compose.yaml pull; then\\n echo \\\"docker compose pull completed\\\"\\n notify_host_hoot_info \\\"docker compose pull completed\\\"\\nelse\\n echo \\\"WARNING: docker compose pull failed; continuing with existing images\\\"\\n notify_host_hoot_info \\\"docker compose pull failed; using existing images\\\"\\nfi\\n\\necho \\\"Images after pull:\\\"\\ndocker images --format '{{.Repository}}:{{.Tag}} {{.ID}} ({{.CreatedSince}})'\\n\\n#\\n# Set root password.\\n#\\necho \\\"Setting root password..\\\"\\n\\n# Check if password files are writable\\nPASSWD_WRITABLE=true\\nif [ ! -w /etc/passwd ]; then\\n echo \\\"Warning: /etc/passwd is read-only\\\"\\n PASSWD_WRITABLE=false\\nfi\\nif [ ! -w /etc/shadow ]; then\\n echo \\\"Warning: /etc/shadow is read-only\\\"\\n PASSWD_WRITABLE=false\\nfi\\n\\nif [ \\\"$PASSWD_WRITABLE\\\" = \\\"false\\\" ]; then\\n echo \\\"Skipping password setup due to read-only file system\\\"\\nelse\\n # Check if chpasswd is available\\n if command -v chpasswd >/dev/null 2>&1; then\\n echo \\\"Using chpasswd method\\\"\\n\\n if [ -n \\\"$DSTACK_ROOT_PASSWORD\\\" ]; then\\n echo \\\"Setting root password from user..\\\"\\n echo \\\"root:$DSTACK_ROOT_PASSWORD\\\" | chpasswd\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set/updated from DSTACK_ROOT_PASSWORD\\\"\\n elif [ -z \\\"$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)\\\" ]; then\\n echo \\\"Setting random root password..\\\"\\n DSTACK_ROOT_PASSWORD=$(\\n LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null\\n )\\n echo \\\"root:$DSTACK_ROOT_PASSWORD\\\" | chpasswd\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set (random auto-init)\\\"\\n else\\n echo \\\"Root password already set; no changes.\\\"\\n fi\\n else\\n echo \\\"Using passwd method\\\"\\n\\n if [ -n \\\"$DSTACK_ROOT_PASSWORD\\\" ]; then\\n echo \\\"Setting root password from user..\\\"\\n echo \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd --stdin root 2>/dev/null || printf '%s\\n%s\\n' \\\"$DSTACK_ROOT_PASSWORD\\\" \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd root\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set/updated from DSTACK_ROOT_PASSWORD\\\"\\n elif [ -z \\\"$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)\\\" ]; then\\n echo \\\"Setting random root password..\\\"\\n DSTACK_ROOT_PASSWORD=$(\\n LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null\\n )\\n echo \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd --stdin root 2>/dev/null || printf '%s\\n%s\\n' \\\"$DSTACK_ROOT_PASSWORD\\\" \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd root\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set (random auto-init)\\\"\\n else\\n echo \\\"Root password already set; no changes.\\\"\\n fi\\n fi\\nfi\\n\\n#\\n# Set SSH authorized keys\\n#\\nif mkdir -p /home/root/.ssh 2>/dev/null; then\\n if [[ -n \\\"$DSTACK_ROOT_PUBLIC_KEY\\\" ]]; then\\n echo \\\"$DSTACK_ROOT_PUBLIC_KEY\\\" > /home/root/.ssh/authorized_keys\\n unset $DSTACK_ROOT_PUBLIC_KEY\\n echo \\\"Root public key set\\\"\\n fi\\n if [[ -n \\\"$DSTACK_AUTHORIZED_KEYS\\\" ]]; then\\n echo \\\"$DSTACK_AUTHORIZED_KEYS\\\" > /home/root/.ssh/authorized_keys\\n unset $DSTACK_AUTHORIZED_KEYS\\n echo \\\"Root authorized_keys set\\\"\\n fi\\n\\n if [[ -f /dstack/user_config ]] && jq empty /dstack/user_config 2>/dev/null; then\\n if [[ $(jq 'has(\\\"ssh_authorized_keys\\\")' /dstack/user_config 2>/dev/null) == \\\"true\\\" ]]; then\\n jq -j '.ssh_authorized_keys' /dstack/user_config >> /home/root/.ssh/authorized_keys\\n # Remove duplicates if there are multiple keys\\n if [[ $(cat /home/root/.ssh/authorized_keys | wc -l) -gt 1 ]]; then\\n sort -u /home/root/.ssh/authorized_keys > /home/root/.ssh/authorized_keys.tmp\\n mv /home/root/.ssh/authorized_keys.tmp /home/root/.ssh/authorized_keys\\n fi\\n echo \\\"Set root authorized_keys from user preferences, total\\\" $(cat /home/root/.ssh/authorized_keys | wc -l) \\\"keys\\\"\\n fi\\n fi\\nelse\\n echo \\\"Warning: Cannot create /home/root/.ssh directory (read-only file system?)\\\"\\n echo \\\"Skipping SSH key setup\\\"\\nfi\\n\\nif [[ -S /var/run/dstack.sock ]]; then\\n export DSTACK_APP_ID=$(curl -s --unix-socket /var/run/dstack.sock http://dstack/Info | jq -j .app_id)\\nelif [[ -S /var/run/tappd.sock ]]; then\\n export DSTACK_APP_ID=$(curl -s --unix-socket /var/run/tappd.sock http://dstack/prpc/Tappd.Info | jq -j .app_id)\\nfi\\n# Check if DSTACK_GATEWAY_DOMAIN is not set, try to get it from user_config or app-compose.json\\n# Priority: user_config > app-compose.json\\nif [[ -z \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]]; then\\n # First try to get from /dstack/user_config if it exists and is valid JSON\\n if [[ -f /dstack/user_config ]] && jq empty /dstack/user_config 2>/dev/null; then\\n if [[ $(jq 'has(\\\"default_gateway_domain\\\")' /dstack/user_config 2>/dev/null) == \\\"true\\\" ]]; then\\n export DSTACK_GATEWAY_DOMAIN=$(jq -j '.default_gateway_domain' /dstack/user_config)\\n fi\\n fi\\n\\n # If still not set, try to get from app-compose.json\\n if [[ -z \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]] && [[ $(jq 'has(\\\"default_gateway_domain\\\")' app-compose.json) == \\\"true\\\" ]]; then\\n export DSTACK_GATEWAY_DOMAIN=$(jq -j '.default_gateway_domain' app-compose.json)\\n fi\\nfi\\nif [[ -n \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]]; then\\n export DSTACK_APP_DOMAIN=$DSTACK_APP_ID\\\".\\\"$DSTACK_GATEWAY_DOMAIN\\nfi\\n\\necho \\\"----------------------------------------------\\\"\\necho \\\"Script execution completed\\\"\\necho \\\"----------------------------------------------\\\"\\n\",\"public_logs\":true,\"public_sysinfo\":true,\"public_tcbinfo\":true,\"runner\":\"docker-compose\",\"secure_time\":false,\"storage_fs\":\"zfs\",\"tproxy_enabled\":true}", "key_custody": {"provider": "dstack-kms", "keys": [{"role": "receipt", "path": "aci/receipt-ed25519/v1", "purpose": "aci.receipt.ed25519.v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a", "kms_public_key": "02f7fb3ed8c352d316dfdc19458a9f29c06eb7e74130b3808d784e2fd401a2c12d", "signature_chain": ["d1f59335effc2688acdf502ee34d8d3373b0e7e6be493075383714ce5673a29973f224690437c483ae3fdd37172df3abad82877c4023ed7c5a64d5acaf18477c00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-secp256k1", "path": "aci/e2ee/v1", "purpose": "aci.e2ee.v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d", "kms_public_key": "03943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f", "signature_chain": ["452d6f86a807fef0ce0d65e6c5cd25a0a249b1fd8608d26c3efe17fc10346f4c2d3dfb8969d10dc1feac1d595b3816f63faa40ac370a3a233ce1d712d418c89701", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-x25519", "path": "aci/e2ee-x25519/v1", "purpose": "aci.e2ee.x25519.v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537", "kms_public_key": "03892f86b3f55f78cc0760cec933cf8e2a92862fb173c2d221cc25895847cbb5eb", "signature_chain": ["358cd661ca4f5e986ed950ca083bf7bcfef6ab4b839ca11a953b156d64f82b1c51a213730d5777b4c845170827680d41e8220c2f950a38081025ff16eb3789ba00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}]}, "downstream_tls_binding": {"domain": "tee.redpill.ai", "spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b"}}}, "service_capabilities": {"supported_e2ee_versions": ["2"], "serving": "aggregator"}}}
\ No newline at end of file
diff --git a/sdk/package.json b/sdk/package.json
index 78b58c4..3e091d4 100644
--- a/sdk/package.json
+++ b/sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@prismnetwork/agent-sdk",
- "version": "0.7.8",
+ "version": "0.7.10",
"description": "Headless GPU leasing and renter-encrypted storage on Prism Network for wallet-holding agents.",
"type": "module",
"main": "prism.mjs",
diff --git a/x402/exact-evm.mjs b/x402/exact-evm.mjs
index f93daa7..55e2f1b 100644
--- a/x402/exact-evm.mjs
+++ b/x402/exact-evm.mjs
@@ -52,6 +52,10 @@ const REASON = {
recipient: "invalid_exact_evm_payload_recipient_mismatch",
funds: "insufficient_funds",
state: "invalid_transaction_state",
+ // The protocol folds a spent nonce into invalid_transaction_state, alongside
+ // every other reason a transfer will not simulate. They need different
+ // answers: a replay is fixed by signing a new authorization, a revert is not.
+ spent: "payment_already_redeemed",
// Not in the protocol's list, because the protocol assumes a facilitator can
// read what it broadcast. Ours must survive an rpc that cannot.
unconfirmed: "settlement_unconfirmed",
@@ -208,7 +212,7 @@ export function createExactEvm(networks) {
]);
// A spent nonce is the contract reporting this exact authorization already
// settled, which is a replay rather than a malformed payload.
- if (used) return { isValid: false, invalidReason: REASON.state, payer };
+ if (used) return { isValid: false, invalidReason: REASON.spent, payer };
if (balance < value) return { isValid: false, invalidReason: REASON.funds, payer };
try {
From e36542d89d13e7fdc3a7526798ba618237d65f4f Mon Sep 17 00:00:00 2001
From: winterstacks
Date: Wed, 16 Sep 2026 12:48:45 +0200
Subject: [PATCH 2/3] keep the captured compose base64 in the fixture
The measured compose carries an upstream path the secret scanner reads as a
leaked local one, which is why the fixture stored it encoded. The attestation
endpoint now returns it plain, so a verbatim recapture reintroduced the trip.
Re-encode on capture; the bytes round trip exactly and the hash still holds.
Signed-off-by: winterstacks
---
python-sdk/tests/test_inference.py | 9 +++++----
sdk/fixtures/aci-attestation.json | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/python-sdk/tests/test_inference.py b/python-sdk/tests/test_inference.py
index dac6f34..d053c5d 100644
--- a/python-sdk/tests/test_inference.py
+++ b/python-sdk/tests/test_inference.py
@@ -933,10 +933,11 @@ class CapturedReportTest(unittest.TestCase):
"""One live report from the public attestation endpoint, as the offline
vector for the replay and binding checks.
- The endpoint returned ``app_compose`` base64-encoded until 2026-09, because
- the measured compose is a shell script whose paths a secret scanner reads as
- leaked local ones. Captures from either era are restored byte for byte here:
- the whole point of the check is that those bytes hash to the measurement.
+ The measured compose is a shell script whose paths a secret scanner reads as
+ leaked local ones, so the fixture stores it base64 and restores it byte for
+ byte here. The endpoint used to return it encoded and now returns it plain,
+ so a recapture has to be re-encoded before it lands in the repo. The whole
+ point of the check is that those bytes hash to the measurement.
"""
@classmethod
diff --git a/sdk/fixtures/aci-attestation.json b/sdk/fixtures/aci-attestation.json
index 86f3326..95c0c22 100644
--- a/sdk/fixtures/aci-attestation.json
+++ b/sdk/fixtures/aci-attestation.json
@@ -1 +1 @@
-{"captured_at": "2026-09-14", "endpoint": "https://tee.redpill.ai/v1/aci/attestation", "nonce": "74823bb0e609a4595fe7e6b166e23522d9da5fddec467ede0f1e1c4508de6856", "note": "app_compose is held base64-encoded: the measured compose is a shell script full of paths, and the repository secret scanner reads those as leaked local paths. The test decodes it before hashing.", "report": {"api_version": "aci/1", "workload_keyset_digest": "sha256:9223dbe01170f56efeae9a72b0df74eeb3b30befecb3ec24d9c11413d1a25510", "attestation": {"tee_type": "tdx", "workload_keyset": {"subject": null, "not_after": 1791969683, "receipt_signing_keys": [{"key_id": "dstack-kms-receipt-ed25519-v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a"}], "e2ee_public_keys": [{"key_id": "dstack-kms-e2ee-v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d"}, {"key_id": "dstack-kms-e2ee-x25519-v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537"}], "tls_public_keys": [{"spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b", "domain": "tee.redpill.ai"}, {"spki_sha256": "8bafb8e6ddc55e3e662b6385a2820494fc6e90c64688136860b1851a81b7ee06", "domain": "inference.phala.com"}, {"spki_sha256": "c68585e7dc00a55d3082a9c860cb916e1788c5daec51505e413d27ab94f7d4c7", "domain": "api.redpill.ai"}, {"spki_sha256": "29a17c8e20ffed55893f6d729a75a7cf34637961d268e870842d1f2f7badd7e5", "domain": "openrouter.phala.com"}]}, "report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc2", "source_provenance": {"repo_url": "https://github.com/Dstack-TEE/private-ai-gateway.git", "repo_commit": "3e56bd30dd459d0df90afeb6d63eca7a919bc22f", "image_digest": null, "image_provenance": null}, "evidence": {"quote": "040002008100000000000000939a7233f79c4ca9940a0db3957f06074d79685452a1c5725d6ff7f9d09c91b4000000000b0105000000000000000000000000007bf063280e94fb051f5dd7b1fc59ce9aac42bb961df8d44b709c9b0ff87a7b4df648657ba6d1189589feab1d5a3c9a9d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000702000000000000f06dfda6dce1cf904d4e2bab1dc370634cf95cefa2ceb2de2eee127c9382698090d7a4a13e14c536ec6c9c3c8fa87077011a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046a83f6bbbeaa274aaf04861ef47d7e8898d4e699126b642f72f582e8d7f5349cb266a188bbabf619940d4fece06594b07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878cdf67e467e60edc1737bcf8e682d48131bfb427f523226aa7f197a7608e9b3784783fa759ef5b28191fa12f9ddb36b85819d97a9f6d54660386dddd1892775007e47134c704e91b2654b056fb2174fa5d547fb0d76bf67e75aaafdd641b182e1367ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000cc100000a7a18da0806034774b872a4fcf4dee8ee566b6af0a55f83acbfc76ba5befe2c5108b444149b24e6520ab118a982f953ac52c809f9dce9ee47a8cebbf53fa4916f0bfa28939a048d1d67a664ae6595ec4514829a10a01f7fc10230dd75c44697bf80c2e1e89a20ca66c1f76795d1cf5553a394c311bcc4601b9231b111d70d6b506004610000005050a0a05ff00020000000000000000000000000000000000000000000000000000000000000000000000000000000015000000000000000700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c1dc25ab6fd4ae8afb815c1c46f6d3f9bbe7ab053e699d259bf9136751037b20000000000000000000000000000000000000000000000000000000000000000a83b9d22a44e8d08fb7c247a64a52059f3b6b832995038b1a3b6578b0fc6fd488012210fe4a3762e3019de96ee5eb63bf1977043834cab40034ee81d35efd4082000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538544343424a656741774942416749555359617753595541735374766a483236625541434d506e6f6a797777436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774d7a49784d4463304e5455795768634e4d7a4d774d7a49784d4463304e5455790a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d34394177454841304941424747690a41664533764c54435150696e5470685047695854516c645948654f6f657257516d4b66745141787939446c63353469446b4b537852786e7a38474f7a466f65690a6b584e532b44374575464864666b476a754e796a67674d4e4d4949444354416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464c6d495a7555304e42506c79336e757041556f41396f590a73625a514d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f67594a4b6f5a496876684e415130420a424949434b7a4343416963774867594b4b6f5a496876684e415130424151515162536e396a546e656c37306e456e776e523951666554434341575147436971470a534962345451454e41514977676746554d42414743797147534962345451454e41514942416745464d42414743797147534962345451454e41514943416745460a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745464d42454743797147534962345451454e41514947416749412f7a415142677371686b69472b453042445145434277494241444151426773710a686b69472b4530424451454343414942416a415142677371686b69472b45304244514543435149424144415142677371686b69472b45304244514543436749420a4144415142677371686b69472b45304244514543437749424144415142677371686b69472b45304244514543444149424144415142677371686b69472b4530420a44514543445149424144415142677371686b69472b45304244514543446749424144415142677371686b69472b453042445145434477494241444151426773710a686b69472b45304244514543454149424144415142677371686b69472b45304244514543455149424454416642677371686b69472b45304244514543456751510a425155434167582f4141494141414141414141414144415142676f71686b69472b45304244514544424149414144415542676f71686b69472b453042445145450a424159676f473841414141774477594b4b6f5a496876684e4151304242516f424154416542676f71686b69472b453042445145474242414e78727a30753075620a48784e354e3964353631576d4d45514743697147534962345451454e415163774e6a415142677371686b69472b45304244514548415145422f7a4151426773710a686b69472b45304244514548416745422f7a415142677371686b69472b45304244514548417745422f7a414b42676771686b6a4f5051514441674e49414442460a4169454131305850553751764338564a743949654f724a366c4430744b58462b54746a4e422f6f7a62635235517477434944576e57773946484e7351745359420a70394c5463792f5670376f517367417076763332732f4236576b73760a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "quote_report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000", "event_log": "[{\"imr\":0,\"event_type\":2147483659,\"digest\":\"5fe4f9ee465a361d077a6892f6bc4d19a82b272fea2e3c2c1ef9c33b4d9050115fca0c6dc7a7f68f91c623de38c48226\",\"event\":\"\",\"event_payload\":\"095464785461626c65000100000000000000af96bb93f2b9b84e9462e0ba745642360090800000000000\"},{\"imr\":0,\"event_type\":2147483658,\"digest\":\"344bc51c980ba621aaa00da3ed7436f7d6e549197dfe699515dfa2c6583d95e6412af21c097d473155875ffd561d6790\",\"event\":\"\",\"event_payload\":\"2946762858585858585858582d585858582d585858582d585858582d58585858585858585858585829000000c0ff000000000040080000000000\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"9dc3a1f80bcec915391dcda5ffbb15e7419f77eab462bbf72b42166fb70d50325e37b36f93537a863769bcf9bedae6fb\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0a00000000000000000000000000000053006500630075007200650042006f006f007400\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"6f2e3cbc14f9def86980f5f66fd85e99d63e69a73014ed8a5633ce56eca5b64b692108c56110e22acadcef58c3250f1b\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0200000000000000000000000000000050004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"d607c0efb41c0d757d69bca0615c3a9ac0b1db06c557d992e906c6b7dee40e0e031640c7bfd7bcd35844ef9edeadc6f9\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c030000000000000000000000000000004b0045004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"08a74f8963b337acb6c93682f934496373679dd26af1089cb4eaf0c30cf260a12e814856385ab8843e56a9acea19e127\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f0200000000000000000000000000000064006200\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"18cc6e01f0c6ea99aa23f8a280423e94ad81d96d0aeb5180504fc0f7a40cb3619dd39bd6a95ec1680a86ed6ab0f9828d\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f03000000000000000000000000000000640062007800\"},{\"imr\":0,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":0,\"event_type\":10,\"digest\":\"febfba94a148ef74278a30ed690856a468636b51f619d44025c4f9dc0212068369895d35c67157ccd2149cd049f3761c\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"5427470ec5de45a3ad085101e7567c7f0b4aa4b0147d9aa583f067fdcb15155fc52a22bd23fd8ea10d045751f8e52337\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"2194a807023a3ce948e26538252c046bce7c76293a3750ac22a41d1c2a3584bd53cc95bbdbde2d0c9f3a906f2663840f\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":1,\"event_type\":2147483651,\"digest\":\"90e278aa4d688f4f05bf76f1786fea174abcea0d6b44e81cea5b3327f3f211e5c465a9c34b513283bed2512bca6d4a19\",\"event\":\"\",\"event_payload\":\"1860fd7a0000000000c485000000000000000000000000002a000000000000000403140072f728144ab61e44b8c39ebdd7f893c7040412006b00650072006e0065006c0000007fff0400\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0900000000000000020000000000000042006f006f0074004f0072006400650072000000\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"23ada07f5261f12f34a0bd8e46760962d6b4d576a416f1fea1c64bc656b1d28eacf7047ae6e967c58fd2a98bfa74c298\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c08000000000000003e0000000000000042006f006f0074003000300030003000090100002c0055006900410070007000000004071400c9bdb87cebf8344faaea3ee4af6516a10406140021aa2c4614760345836e8ab6f46623317fff0400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71\",\"event\":\"\",\"event_payload\":\"43616c6c696e6720454649204170706c69636174696f6e2066726f6d20426f6f74204f7074696f6e\"},{\"imr\":1,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":2,\"event_type\":6,\"digest\":\"9c824b958dbbef07ec732fd79727e995d5eca309dbc6d62e4fd2840f267c5e87108b5fe25c7997c67801ff391fb67ffc\",\"event\":\"\",\"event_payload\":\"ed223b8f1a0000004c4f414445445f494d4147453a3a4c6f61644f7074696f6e7300\"},{\"imr\":2,\"event_type\":6,\"digest\":\"5eb4f34f2ec49071551c5562ebaab9f86408f4a0eadaa3d7f05157e37ab04a1f040a82eaae94015d628a7ea4aa482cd2\",\"event\":\"\",\"event_payload\":\"ec223b8f0d0000004c696e757820696e6974726400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0\",\"event\":\"\",\"event_payload\":\"4578697420426f6f7420536572766963657320496e766f636174696f6e\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74\",\"event\":\"\",\"event_payload\":\"4578697420426f6f742053657276696365732052657475726e656420776974682053756363657373\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"f9974020ef507068183313d0ca808e0d1ca9b2d1ad0c61f5784e7157c362c06536f5ddacdad4451693f48fcc72fff624\",\"event\":\"system-preparing\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"8562fc3980258ea5119a31356844547c42ba696d73f6ecdc7d29732beef0083f22674ce14349a8a1400254466e851407\",\"event\":\"app-id\",\"event_payload\":\"fdb7a14e5a6675f752e2cb69c9067a98ca402918\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"5edb4d5fbf301c47c77faa3de6edbcb97b93521b7c63ac6a8d8028d2f1891bd8517d76285f6e724cf28451acaac6c89e\",\"event\":\"compose-hash\",\"event_payload\":\"1a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"dbb7d7c2c2ce19938fb5989949f2c4bba6829ced5cd7b8c0d98696c293ac1c5dc8b1bfb9bc95b61e2ff778a3686651eb\",\"event\":\"instance-id\",\"event_payload\":\"15e847e02a22706121de347498427469d2e7c379\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"98bd7e6bd3952720b65027fd494834045d06b4a714bf737a06b874638b3ea00ff402f7f583e3e3b05e921c8570433ac6\",\"event\":\"boot-mr-done\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"65828c6f922910fd0e272501a8b2bb6b03e24c99bb19d1cd530586061d8738f9dd0564752db23d556a6b381cbf465a85\",\"event\":\"mr-kms\",\"event_payload\":\"692afc6d7a86a32cfc1ebd9cad1a576aab012bab46986ba609bc8d6407270572\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"c936f709860155a455c519b505fe29bde21d1b778534a8f0b41b706b0f789eae2ea899e224fedb2331c18324aab4db93\",\"event\":\"os-image-hash\",\"event_payload\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"83368b43a0fc6f824f5a9220592df85fd30e2d405ecbd253a5c6354af63e6c9b41aec557c38a38e348ab87f9ac8fc68c\",\"event\":\"key-provider\",\"event_payload\":\"7b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343838343465623432636364663863353266643466313734663336326663623962626431396334356664343866316564656332643866316361323335333665633161373430323162346365653631306330373466383239346434333162326237666565326333396535333333666461663061343532326434336662313539643966227d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"ba51104636900268b0e059fa3d266419d079d1e94aea26fb9fcbb8d764bf4c89a67ac271b8a0d1a3989945132a111fc7\",\"event\":\"storage-fs\",\"event_payload\":\"7a6673\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"1a76b2a80a0be71eae59f80945d876351a7a3fb8e9fd1ff1cede5734aa84ea11fd72b4edfbb6f04e5a85edd114c751bd\",\"event\":\"system-ready\",\"event_payload\":\"\"}]", "vm_config": "{\"os_image_hash\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\",\"cpu_count\":32,\"memory_size\":34359738368,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.5.9-bd369a8c\",\"host_share_mode\":\"9p\",\"spec_version\":1}", "app_compose": "{\"allowed_envs\":[\"PRIVATE_AI_GATEWAY_ADMIN_TOKEN\",\"PRIVATE_AI_GATEWAY_CONTROL_URL\",\"PRIVATE_AI_GATEWAY_CONTROL_TOKEN\",\"DSTACK_ROOT_PUBLIC_KEY\",\"CLOUDFLARE_API_TOKEN\",\"CERTBOT_EMAIL\",\"NODE_EXPORTER_USER\",\"NODE_EXPORTER_PASSWORD_HASH\",\"PRIVATE_AI_GATEWAY_PULL_TOKEN\"],\"docker_compose_file\":\"# dstack / Phala deployment manifest for the private-ai-gateway stack via the launcher.\\n#\\n# The launcher image is the measured trust-root: pin it by @sha256 digest (the\\n# digest + this file are hashed into the TDX quote). The launcher clones+builds\\n# the gateway from source at boot; the attestation verifier is\\n# the gateway's vendored confidential_verifier package, so it needs no separate pin.\\n#\\n# The gateway's source pin + static config are inlined as compose `configs:`\\n# (measured into the attestation): the launcher clones the pin and the gateway\\n# reads the config from mounted files. Secret values are supplied as dstack\\n# ENCRYPTED SECRETS and referenced as ${VAR} so dstack injects them \u2014 only the\\n# variable names appear in this public file, never the values.\\n\\nservices:\\n # TLS-terminating ingress: requests CAA-pinned Let's Encrypt certs via Cloudflare\\n # DNS-01, registers under the dstack gateway domain, and reverse-proxies inbound\\n # 443 to the launcher's gateway on :8086. Pin by @sha256 digest like the launcher.\\n dstack-ingress:\\n image: dstacktee/dstack-ingress:2.2@sha256:d05a7b343c37c1cca1bba8dbf7e8f3c6d2118158af2d41c455103796db4f67f0\\n restart: unless-stopped\\n # The ingress is an L4 TCP proxy: every client connection maps 1:1 to a\\n # backend connection to the gateway, so it holds 2 fds per live connection.\\n # haproxy has no `ulimit-n` in its config; it derives an fd need from MAXCONN\\n # and raises its OWN soft nofile from the container hard limit. Pin the hard\\n # limit here (like `launcher`) so MAXCONN is never silently capped by whatever\\n # the docker daemon default happens to be. 65536 comfortably covers\\n # MAXCONN 16384 (~33k fds) with headroom.\\n ulimits:\\n nofile:\\n soft: 65536\\n hard: 65536\\n ports:\\n - \\\"443:443\\\"\\n environment:\\n # --- public config: literals, measured into the attestation ---\\n DNS_PROVIDER: cloudflare\\n GATEWAY_DOMAIN: _.${DSTACK_GATEWAY_DOMAIN}\\n SET_CAA: \\\"true\\\"\\n # Concurrent-connection ceiling. The image default is 4096; under real\\n # streaming load the ingress sat pinned at that ceiling ~84% of the time\\n # (avg ~3720 concurrent), so once a burst pushed past 4096 haproxy stopped\\n # accepting and clients saw a hang. Streaming LLM traffic keeps thousands of\\n # long-lived connections open at once, so give a 4x headroom. Must stay well\\n # under the nofile hard limit above (2 fds/conn in TCP mode).\\n MAXCONN: \\\"16384\\\"\\n # Idle (no-data) timeout, reset on any byte in either direction. The image\\n # default is 86400s (24h): a client that stops reading, or an upstream that\\n # goes silent, keeps its slot for a full day, which is how slots leaked\\n # toward the ceiling. 1800s reaps a genuinely idle/stalled connection while\\n # never touching a stream that is still flowing (even a slow one). NOTE:\\n # this is the one value to confirm against the longest legitimate silent\\n # gap a request can have \u2014 e.g. a reasoning model that thinks >30 min before\\n # its first token would be cut. Raise if such requests are expected.\\n TIMEOUT_CLIENT: 1800s\\n TIMEOUT_SERVER: 1800s\\n # Multi-domain TLS. The ingress is an L4 (TCP) proxy: it terminates TLS for\\n # each hostname and forwards the DECRYPTED stream untouched, so the client's\\n # original HTTP Host survives. The gateway uses that Host to pick the\\n # matching downstream_tls_binding. All public hostnames route by SNI to the\\n # same gateway listener (compose service name, not localhost). tee.redpill.ai\\n # and inference.phala.com are the TEE-only domains (see `tee_only_domains`\\n # below); api.redpill.ai and openrouter.phala.com are the non-TEE domains.\\n DOMAINS: |\\n tee.redpill.ai\\n inference.phala.com\\n api.redpill.ai\\n openrouter.phala.com\\n ROUTING_MAP: |\\n tee.redpill.ai=launcher:8086\\n inference.phala.com=launcher:8086\\n api.redpill.ai=launcher:8086\\n openrouter.phala.com=launcher:8086\\n # --- secrets: values come from dstack Encrypted Secrets (never commit values) ---\\n # NOTE: this single Cloudflare token must have DNS-edit (and CAA) rights on\\n # BOTH the redpill.ai and phala.com zones, or cert issuance for one fails.\\n CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}\\n CERTBOT_EMAIL: ${CERTBOT_EMAIL}\\n volumes:\\n - /var/run/dstack.sock:/var/run/dstack.sock\\n - /var/run/tappd.sock:/var/run/tappd.sock\\n - cert-data:/etc/letsencrypt\\n # Per-domain leaf certs (cert-.pem) the gateway reads to bind SPKI.\\n - evidences:/evidences\\n\\n launcher:\\n image: ghcr.io/redpill-ai/private-ai-launcher@sha256:c083ff9e6a5ddf10f6c9e9bb1f74cc618deebecfea5208b563c574399db4637c\\n restart: always\\n # The gateway opens many fds (inbound conns, per-upstream pools, control-plane\\n # + verifier calls). Docker's default nofile (often 1024) is far too low: under\\n # load it hit EMFILE (\\\"Too many open files\\\"), which killed the gateway's accept\\n # loop. Raise the limit well above peak concurrency.\\n ulimits:\\n nofile:\\n soft: 65536\\n hard: 65536\\n # supervisord streams the gateway logs to the container stdout; cap the\\n # runtime log on disk so it can't grow unbounded (~5GB total, rotated).\\n # Sized for the gateway's always-on request_outcome failure logging:\\n # roughly a month of retention on the persistent volume for incident\\n # forensics.\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"500m\\\"\\n max-file: \\\"10\\\"\\n # no host port: traffic enters via dstack-ingress (443) and reaches the\\n # gateway over the compose network as http://launcher:8086.\\n expose:\\n - \\\"8086\\\"\\n volumes:\\n - /var/run/dstack.sock:/var/run/dstack.sock # only the gateway uses this\\n - pal-cache:/var/lib/pag/cache # cargo/uv build cache (speeds reboots)\\n - pal-state:/var/lib/pag/state # mutable upstreams.json + sessions.jsonl\\n - evidences:/evidences:ro # ingress-minted per-domain leaf certs (cert-.pem)\\n # Static gateway config + upstream seed are mounted from the `configs:` block\\n # at the bottom (measured into the attestation). The source pin is passed via\\n # `environment:` instead (see below).\\n configs:\\n - source: gateway-config\\n target: /etc/private-ai-gateway/gateway.config.json\\n - source: gateway-upstreams\\n target: /etc/private-ai-gateway/upstreams.seed.json\\n environment:\\n # The gateway reads its whole policy from this mounted static config file\\n # (see `configs:` above); CONFIG_PATH is the only gateway-facing env var.\\n - PRIVATE_AI_GATEWAY_CONFIG_PATH=/etc/private-ai-gateway/gateway.config.json\\n # Tracing filter. `request_outcome=debug` turns on the raw error-detail\\n # field of the per-request failure log \u2014 a deliberate operator choice:\\n # upstream error bodies may echo request fragments into these\\n # operator-visible logs. Drop the `request_outcome=debug` part to fall\\n # back to content-free failure logging only.\\n - RUST_LOG=info,request_outcome=debug\\n # Gateway source pin. These literals are measured into the attestation, and\\n # because they live in `environment:` (part of Compose's config-hash) a\\n # COMMIT_SHA bump forces a container recreate on `phala deploy` \u2014 so the new\\n # pin actually takes effect. (A `configs:`-file pin would not: Compose ignores\\n # config content when deciding to recreate, so the change would never apply.)\\n # entrypoint.sh writes /etc/git-launcher/gateway.conf from these, and the\\n # gateway reads that file as its attested source provenance.\\n - PRIVATE_AI_GATEWAY_REPO_URL=https://github.com/Dstack-TEE/private-ai-gateway.git\\n - PRIVATE_AI_GATEWAY_REPO_COMMIT=3e56bd30dd459d0df90afeb6d63eca7a919bc22f\\n # NEAR AI verification posts TDX quotes to the dstack-verifier sidecar below;\\n # reach it by compose service name (localhost would be the launcher itself).\\n - DSTACK_VERIFIER_URL=http://dstack-verifier:8080\\n # --- secrets: values come from dstack Encrypted Secrets (never commit values) ---\\n # Control-plane credentials. The gateway's in-process middleware reads these\\n # from the `middleware` config section below (rendered from the same secrets).\\n - PRIVATE_AI_GATEWAY_CONTROL_URL=${PRIVATE_AI_GATEWAY_CONTROL_URL}\\n - PRIVATE_AI_GATEWAY_CONTROL_TOKEN=${PRIVATE_AI_GATEWAY_CONTROL_TOKEN}\\n\\n # TDX quote verifier for the NEAR AI path (reproduces ACPI/boot measurements via\\n # QEMU). The gateway reaches it at http://dstack-verifier:8080 (DSTACK_VERIFIER_URL).\\n dstack-verifier:\\n image: dstacktee/dstack-verifier:0.6.0-rc3@sha256:0ed61d60f6e0d44cf466e15dc6894f37152d8567cfa76bd8785a921b2d906c38\\n restart: unless-stopped\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"50m\\\"\\n max-file: \\\"5\\\"\\n\\n # Host-metrics exporter for observing this CVM (CPU/mem/disk/net/fd). Inside a\\n # CVM the container's /, /proc, /sys ARE the guest's, so node-exporter reports\\n # the CVM's own state. Pinned by @sha256 like the other images (measured into\\n # the attestation). Exposed on 9100 (reached via the dstack gateway) and guarded\\n # by HTTP basic auth \u2014 both username and the bcrypt password HASH are injected as\\n # dstack Encrypted Secrets, so neither appears in this measured file.\\n node-exporter:\\n image: prom/node-exporter:v1.8.2@sha256:4032c6d5bfd752342c3e631c2f1de93ba6b86c41db6b167b9a35372c139e7706\\n restart: unless-stopped\\n pid: host\\n command:\\n - '--path.rootfs=/host'\\n - '--path.procfs=/host/proc'\\n - '--path.sysfs=/host/sys'\\n - '--web.config.file=/etc/node-exporter/web-config.yml'\\n volumes:\\n - /:/host:ro,rslave\\n configs:\\n - source: node-exporter-web-config\\n target: /etc/node-exporter/web-config.yml\\n ports:\\n - \\\"9100:9100\\\"\\n logging:\\n driver: json-file\\n options:\\n max-size: \\\"10m\\\"\\n max-file: \\\"3\\\"\\n\\nvolumes:\\n pal-cache:\\n pal-state:\\n cert-data: # Let's Encrypt certs/keys persisted for dstack-ingress\\n evidences: # ingress attestation evidence + per-domain leaf certs, shared read-only with the gateway\\n\\n# Inlined, measured into the attestation. Public pins use LITERAL values so the\\n# code version is bound into compose_hash; only secrets use ${VAR} (injected by\\n# dstack, never measured).\\nconfigs:\\n # Static gateway config. `admin_token`, the dedicated upstream pull token, and\\n # the control-plane credentials are secrets \u2014 ${VAR} keeps their values out of\\n # the measured compose while the presence and destination stay measured. The\\n # `middleware` section runs the in-process middleware, which consults the control\\n # plane at `control_url`.\\n gateway-config:\\n content: |\\n {\\n \\\"bind\\\": \\\"0.0.0.0:8086\\\",\\n \\\"state_dir\\\": \\\"/var/lib/pag/state\\\",\\n \\\"upstream_config_seed_path\\\": \\\"/etc/private-ai-gateway/upstreams.seed.json\\\",\\n \\\"upstream_pull\\\": {\\n \\\"url\\\": \\\"https://service.redpill.ai/api/admin/gateway-upstreams/config\\\",\\n \\\"token\\\": \\\"${PRIVATE_AI_GATEWAY_PULL_TOKEN:?set PRIVATE_AI_GATEWAY_PULL_TOKEN}\\\",\\n \\\"refresh_seconds\\\": 300,\\n \\\"request_timeout_seconds\\\": 90\\n },\\n \\\"admin_token\\\": \\\"${PRIVATE_AI_GATEWAY_ADMIN_TOKEN:?set PRIVATE_AI_GATEWAY_ADMIN_TOKEN}\\\",\\n \\\"tls\\\": {\\n \\\"domain_certificates\\\": [\\n { \\\"domain\\\": \\\"tee.redpill.ai\\\", \\\"certificate_path\\\": \\\"/evidences/cert-tee.redpill.ai.pem\\\" },\\n { \\\"domain\\\": \\\"inference.phala.com\\\", \\\"certificate_path\\\": \\\"/evidences/cert-inference.phala.com.pem\\\" },\\n { \\\"domain\\\": \\\"api.redpill.ai\\\", \\\"certificate_path\\\": \\\"/evidences/cert-api.redpill.ai.pem\\\" },\\n { \\\"domain\\\": \\\"openrouter.phala.com\\\", \\\"certificate_path\\\": \\\"/evidences/cert-openrouter.phala.com.pem\\\" }\\n ]\\n },\\n \\\"middleware\\\": {\\n \\\"control_url\\\": \\\"${PRIVATE_AI_GATEWAY_CONTROL_URL:?set PRIVATE_AI_GATEWAY_CONTROL_URL}\\\",\\n \\\"control_token\\\": \\\"${PRIVATE_AI_GATEWAY_CONTROL_TOKEN:?set PRIVATE_AI_GATEWAY_CONTROL_TOKEN}\\\",\\n \\\"tee_only_domains\\\": [\\\"tee.redpill.ai\\\", \\\"inference.phala.com\\\"]\\n }\\n }\\n # Initial upstream routes: seeded into /upstreams.json only when empty,\\n # then managed at runtime via PUT /v1/admin/upstreams (does not touch this file).\\n gateway-upstreams:\\n content: |\\n []\\n # node-exporter basic-auth credentials. exporter-toolkit's basic_auth_users maps\\n # username -> bcrypt hash; both are ${VAR} so dstack substitutes the injected\\n # secret values when rendering this file into the container \u2014 only the literals\\n # are measured. NODE_EXPORTER_PASSWORD_HASH must be a bcrypt hash\\n # (`htpasswd -nBC 10 \\\"\\\" | tr -d ':\\\\n'`), NOT the plaintext password; the plaintext\\n # goes to the external Prometheus scrape config.\\n node-exporter-web-config:\\n content: |\\n basic_auth_users:\\n ${NODE_EXPORTER_USER:?set NODE_EXPORTER_USER}: ${NODE_EXPORTER_PASSWORD_HASH:?set NODE_EXPORTER_PASSWORD_HASH}\\n\",\"features\":[\"kms\",\"tproxy-net\"],\"gateway_enabled\":true,\"kms_enabled\":true,\"local_key_provider_enabled\":false,\"manifest_version\":2,\"name\":\"\",\"no_instance_id\":false,\"pre_launch_script\":\"#!/bin/bash\\necho \\\"----------------------------------------------\\\"\\necho \\\"Running Phala Cloud Pre-Launch Script v0.0.15\\\"\\necho \\\"----------------------------------------------\\\"\\nset -e\\n\\n# Function: notify host\\n\\nnotify_host() {\\n if command -v dstack-util >/dev/null 2>&1; then\\n dstack-util notify-host -e \\\"$1\\\" -d \\\"$2\\\"\\n else\\n tdxctl notify-host -e \\\"$1\\\" -d \\\"$2\\\"\\n fi\\n}\\n\\nnotify_host_hoot_info() {\\n notify_host \\\"boot.progress\\\" \\\"$1\\\"\\n}\\n\\nnotify_host_hoot_error() {\\n notify_host \\\"boot.error\\\" \\\"$1\\\"\\n}\\n\\n# Function: Perform Docker cleanup\\nperform_cleanup() {\\n echo \\\"Pruning unused images\\\"\\n docker image prune -af\\n echo \\\"Pruning unused volumes\\\"\\n docker volume prune -f\\n notify_host_hoot_info \\\"docker cleanup completed\\\"\\n}\\n\\n# Function: Check Docker login status without exposing credentials\\ncheck_docker_login() {\\n local registry=\\\"$1\\\"\\n\\n # When registry is specified, check auth entry for that registry in Docker config\\n if [[ -n \\\"$registry\\\" ]]; then\\n local docker_config_path=\\\"${DOCKER_CONFIG:-$HOME/.docker}/config.json\\\"\\n if [[ -f \\\"$docker_config_path\\\" ]] && grep -q \\\"$registry\\\" \\\"$docker_config_path\\\"; then\\n return 0\\n else\\n return 1\\n fi\\n fi\\n\\n # Fallback check when no explicit registry is provided\\n if docker info 2>/dev/null | grep -q \\\"Username\\\"; then\\n return 0\\n else\\n return 1\\n fi\\n}\\n\\n# Main logic starts here\\necho \\\"Starting login process...\\\"\\n\\n# Check if Docker credentials exist\\nif [[ -n \\\"$DSTACK_DOCKER_USERNAME\\\" && -n \\\"$DSTACK_DOCKER_PASSWORD\\\" ]]; then\\n echo \\\"Docker credentials found\\\"\\n DOCKER_REGISTRY_TARGET=\\\"${DSTACK_DOCKER_REGISTRY:-docker.io}\\\"\\n echo \\\"Target Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n\\n # Check if already logged in\\n if check_docker_login \\\"$DSTACK_DOCKER_REGISTRY\\\"; then\\n echo \\\"Already logged in to Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n else\\n echo \\\"Logging in to Docker registry: $DOCKER_REGISTRY_TARGET\\\"\\n # Login without exposing password in process list\\n if [[ -n \\\"$DSTACK_DOCKER_REGISTRY\\\" ]]; then\\n echo \\\"$DSTACK_DOCKER_PASSWORD\\\" | docker login -u \\\"$DSTACK_DOCKER_USERNAME\\\" --password-stdin \\\"$DSTACK_DOCKER_REGISTRY\\\"\\n else\\n echo \\\"$DSTACK_DOCKER_PASSWORD\\\" | docker login -u \\\"$DSTACK_DOCKER_USERNAME\\\" --password-stdin\\n fi\\n\\n if [ $? -eq 0 ]; then\\n echo \\\"Docker login successful: $DOCKER_REGISTRY_TARGET\\\"\\n else\\n echo \\\"Docker login failed: $DOCKER_REGISTRY_TARGET\\\"\\n notify_host_hoot_error \\\"docker login failed\\\"\\n exit 1\\n fi\\n fi\\n# Check if AWS ECR credentials exist\\nelif [[ -n \\\"$DSTACK_AWS_ACCESS_KEY_ID\\\" && -n \\\"$DSTACK_AWS_SECRET_ACCESS_KEY\\\" && -n \\\"$DSTACK_AWS_REGION\\\" && -n \\\"$DSTACK_AWS_ECR_REGISTRY\\\" ]]; then\\n echo \\\"AWS ECR credentials found\\\"\\n\\n # Check if AWS CLI is installed\\n if [ ! -f \\\"./aws/dist/aws\\\" ]; then\\n notify_host_hoot_info \\\"awscli not installed, installing...\\\"\\n echo \\\"AWS CLI not installed, installing...\\\"\\n curl \\\"https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.24.14.zip\\\" -o \\\"awscliv2.zip\\\"\\n echo \\\"6ff031a26df7daebbfa3ccddc9af1450 awscliv2.zip\\\" | md5sum -c\\n if [ $? -ne 0 ]; then\\n echo \\\"MD5 checksum failed\\\"\\n notify_host_hoot_error \\\"awscli install failed\\\"\\n exit 1\\n fi\\n unzip awscliv2.zip &> /dev/null\\n else\\n echo \\\"AWS CLI is already installed: ./aws/dist/aws\\\"\\n fi\\n\\n # Set AWS credentials as environment variables\\n export AWS_ACCESS_KEY_ID=\\\"$DSTACK_AWS_ACCESS_KEY_ID\\\"\\n export AWS_SECRET_ACCESS_KEY=\\\"$DSTACK_AWS_SECRET_ACCESS_KEY\\\"\\n export AWS_DEFAULT_REGION=\\\"$DSTACK_AWS_REGION\\\"\\n\\n # Set session token if provided (for temporary credentials)\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"AWS session token found, using temporary credentials\\\"\\n export AWS_SESSION_TOKEN=\\\"$DSTACK_AWS_SESSION_TOKEN\\\"\\n fi\\n\\n # Test AWS credentials before attempting ECR login\\n echo \\\"Testing AWS credentials...\\\"\\n if ! ./aws/dist/aws sts get-caller-identity &> /dev/null; then\\n echo \\\"AWS credentials test failed\\\"\\n # For session token credentials, this might be expected if they're expired\\n # Log warning but don't fail startup\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"Warning: AWS temporary credentials may have expired, continuing startup\\\"\\n notify_host_hoot_info \\\"AWS temporary credentials may have expired\\\"\\n else\\n echo \\\"AWS credentials test failed\\\"\\n notify_host_hoot_error \\\"Invalid AWS credentials\\\"\\n exit 1\\n fi\\n else\\n echo \\\"Logging in to AWS ECR...\\\"\\n ./aws/dist/aws ecr get-login-password --region $DSTACK_AWS_REGION | docker login --username AWS --password-stdin \\\"$DSTACK_AWS_ECR_REGISTRY\\\"\\n if [ $? -eq 0 ]; then\\n echo \\\"AWS ECR login successful\\\"\\n notify_host_hoot_info \\\"AWS ECR login successful\\\"\\n else\\n echo \\\"AWS ECR login failed\\\"\\n # For session token credentials, don't fail startup if login fails\\n if [[ -n \\\"$DSTACK_AWS_SESSION_TOKEN\\\" ]]; then\\n echo \\\"Warning: AWS ECR login failed with temporary credentials, continuing startup\\\"\\n notify_host_hoot_info \\\"AWS ECR login failed with temporary credentials\\\"\\n else\\n notify_host_hoot_error \\\"AWS ECR login failed\\\"\\n exit 1\\n fi\\n fi\\n fi\\nfi\\n\\nperform_cleanup\\n\\n#\\n# GHCR image pull access verification (pure HTTP, no docker daemon)\\n#\\nif [[ \\\"$DOCKER_REGISTRY_TARGET\\\" == \\\"ghcr.io\\\" && -n \\\"$DSTACK_DOCKER_USERNAME\\\" && -n \\\"$DSTACK_DOCKER_PASSWORD\\\" ]]; then\\n COMPOSE_IMAGES=$(grep 'image:' /dstack/docker-compose.yaml 2>/dev/null | awk '{print $2}' | tr -d '\\\"'\\\"'\\\" || true)\\n for img in $COMPOSE_IMAGES; do\\n [[ \\\"$img\\\" != ghcr.io/* ]] && continue\\n repo=\\\"${img#ghcr.io/}\\\"; repo=\\\"${repo%%:*}\\\"\\n tag=\\\"${img##*:}\\\"; [[ \\\"$tag\\\" == \\\"$img\\\" || \\\"$tag\\\" == \\\"$repo\\\" ]] && tag=\\\"latest\\\"\\n echo \\\"Verifying GHCR pull access: $img\\\"\\n token=$(curl -sf -u \\\"$DSTACK_DOCKER_USERNAME:$DSTACK_DOCKER_PASSWORD\\\" \\\"https://ghcr.io/token?service=ghcr.io&scope=repository:${repo}:pull\\\" | jq -r '.token // empty' || true)\\n if [[ -z \\\"$token\\\" ]]; then\\n echo \\\"ERROR: GHCR token exchange failed for $img\\\"\\n notify_host_hoot_error \\\"GHCR token exchange failed: $img\\\"\\n exit 1\\n fi\\n http_code=$(curl -s -o /dev/null -w \\\"%{http_code}\\\" -H \\\"Authorization: Bearer $token\\\" -H \\\"Accept: application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json\\\" \\\"https://ghcr.io/v2/${repo}/manifests/${tag}\\\")\\n if [[ \\\"$http_code\\\" != \\\"200\\\" ]]; then\\n echo \\\"ERROR: GHCR pull access denied for $img (HTTP $http_code)\\\"\\n notify_host_hoot_error \\\"GHCR pull access denied: $img (HTTP $http_code)\\\"\\n exit 1\\n fi\\n echo \\\"GHCR pull access OK: $img\\\"\\n done\\nfi\\n\\n#\\n# Pull latest images from docker-compose.yaml so existing CVMs pick up new tags.\\n# Pull is fail-soft: short-lived tokens may expire between login and pull, but\\n# falling back to already-cached images is preferable to blocking boot.\\n#\\necho \\\"Images before pull:\\\"\\ndocker images --format '{{.Repository}}:{{.Tag}} {{.ID}} ({{.CreatedSince}})'\\n\\necho \\\"Pulling latest images from /dstack/docker-compose.yaml...\\\"\\nif docker compose -f /dstack/docker-compose.yaml pull; then\\n echo \\\"docker compose pull completed\\\"\\n notify_host_hoot_info \\\"docker compose pull completed\\\"\\nelse\\n echo \\\"WARNING: docker compose pull failed; continuing with existing images\\\"\\n notify_host_hoot_info \\\"docker compose pull failed; using existing images\\\"\\nfi\\n\\necho \\\"Images after pull:\\\"\\ndocker images --format '{{.Repository}}:{{.Tag}} {{.ID}} ({{.CreatedSince}})'\\n\\n#\\n# Set root password.\\n#\\necho \\\"Setting root password..\\\"\\n\\n# Check if password files are writable\\nPASSWD_WRITABLE=true\\nif [ ! -w /etc/passwd ]; then\\n echo \\\"Warning: /etc/passwd is read-only\\\"\\n PASSWD_WRITABLE=false\\nfi\\nif [ ! -w /etc/shadow ]; then\\n echo \\\"Warning: /etc/shadow is read-only\\\"\\n PASSWD_WRITABLE=false\\nfi\\n\\nif [ \\\"$PASSWD_WRITABLE\\\" = \\\"false\\\" ]; then\\n echo \\\"Skipping password setup due to read-only file system\\\"\\nelse\\n # Check if chpasswd is available\\n if command -v chpasswd >/dev/null 2>&1; then\\n echo \\\"Using chpasswd method\\\"\\n\\n if [ -n \\\"$DSTACK_ROOT_PASSWORD\\\" ]; then\\n echo \\\"Setting root password from user..\\\"\\n echo \\\"root:$DSTACK_ROOT_PASSWORD\\\" | chpasswd\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set/updated from DSTACK_ROOT_PASSWORD\\\"\\n elif [ -z \\\"$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)\\\" ]; then\\n echo \\\"Setting random root password..\\\"\\n DSTACK_ROOT_PASSWORD=$(\\n LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null\\n )\\n echo \\\"root:$DSTACK_ROOT_PASSWORD\\\" | chpasswd\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set (random auto-init)\\\"\\n else\\n echo \\\"Root password already set; no changes.\\\"\\n fi\\n else\\n echo \\\"Using passwd method\\\"\\n\\n if [ -n \\\"$DSTACK_ROOT_PASSWORD\\\" ]; then\\n echo \\\"Setting root password from user..\\\"\\n echo \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd --stdin root 2>/dev/null || printf '%s\\n%s\\n' \\\"$DSTACK_ROOT_PASSWORD\\\" \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd root\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set/updated from DSTACK_ROOT_PASSWORD\\\"\\n elif [ -z \\\"$(grep '^root:' /etc/shadow 2>/dev/null | cut -d: -f2)\\\" ]; then\\n echo \\\"Setting random root password..\\\"\\n DSTACK_ROOT_PASSWORD=$(\\n LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | dd bs=1 count=32 2>/dev/null\\n )\\n echo \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd --stdin root 2>/dev/null || printf '%s\\n%s\\n' \\\"$DSTACK_ROOT_PASSWORD\\\" \\\"$DSTACK_ROOT_PASSWORD\\\" | passwd root\\n unset DSTACK_ROOT_PASSWORD\\n echo \\\"Root password set (random auto-init)\\\"\\n else\\n echo \\\"Root password already set; no changes.\\\"\\n fi\\n fi\\nfi\\n\\n#\\n# Set SSH authorized keys\\n#\\nif mkdir -p /home/root/.ssh 2>/dev/null; then\\n if [[ -n \\\"$DSTACK_ROOT_PUBLIC_KEY\\\" ]]; then\\n echo \\\"$DSTACK_ROOT_PUBLIC_KEY\\\" > /home/root/.ssh/authorized_keys\\n unset $DSTACK_ROOT_PUBLIC_KEY\\n echo \\\"Root public key set\\\"\\n fi\\n if [[ -n \\\"$DSTACK_AUTHORIZED_KEYS\\\" ]]; then\\n echo \\\"$DSTACK_AUTHORIZED_KEYS\\\" > /home/root/.ssh/authorized_keys\\n unset $DSTACK_AUTHORIZED_KEYS\\n echo \\\"Root authorized_keys set\\\"\\n fi\\n\\n if [[ -f /dstack/user_config ]] && jq empty /dstack/user_config 2>/dev/null; then\\n if [[ $(jq 'has(\\\"ssh_authorized_keys\\\")' /dstack/user_config 2>/dev/null) == \\\"true\\\" ]]; then\\n jq -j '.ssh_authorized_keys' /dstack/user_config >> /home/root/.ssh/authorized_keys\\n # Remove duplicates if there are multiple keys\\n if [[ $(cat /home/root/.ssh/authorized_keys | wc -l) -gt 1 ]]; then\\n sort -u /home/root/.ssh/authorized_keys > /home/root/.ssh/authorized_keys.tmp\\n mv /home/root/.ssh/authorized_keys.tmp /home/root/.ssh/authorized_keys\\n fi\\n echo \\\"Set root authorized_keys from user preferences, total\\\" $(cat /home/root/.ssh/authorized_keys | wc -l) \\\"keys\\\"\\n fi\\n fi\\nelse\\n echo \\\"Warning: Cannot create /home/root/.ssh directory (read-only file system?)\\\"\\n echo \\\"Skipping SSH key setup\\\"\\nfi\\n\\nif [[ -S /var/run/dstack.sock ]]; then\\n export DSTACK_APP_ID=$(curl -s --unix-socket /var/run/dstack.sock http://dstack/Info | jq -j .app_id)\\nelif [[ -S /var/run/tappd.sock ]]; then\\n export DSTACK_APP_ID=$(curl -s --unix-socket /var/run/tappd.sock http://dstack/prpc/Tappd.Info | jq -j .app_id)\\nfi\\n# Check if DSTACK_GATEWAY_DOMAIN is not set, try to get it from user_config or app-compose.json\\n# Priority: user_config > app-compose.json\\nif [[ -z \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]]; then\\n # First try to get from /dstack/user_config if it exists and is valid JSON\\n if [[ -f /dstack/user_config ]] && jq empty /dstack/user_config 2>/dev/null; then\\n if [[ $(jq 'has(\\\"default_gateway_domain\\\")' /dstack/user_config 2>/dev/null) == \\\"true\\\" ]]; then\\n export DSTACK_GATEWAY_DOMAIN=$(jq -j '.default_gateway_domain' /dstack/user_config)\\n fi\\n fi\\n\\n # If still not set, try to get from app-compose.json\\n if [[ -z \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]] && [[ $(jq 'has(\\\"default_gateway_domain\\\")' app-compose.json) == \\\"true\\\" ]]; then\\n export DSTACK_GATEWAY_DOMAIN=$(jq -j '.default_gateway_domain' app-compose.json)\\n fi\\nfi\\nif [[ -n \\\"$DSTACK_GATEWAY_DOMAIN\\\" ]]; then\\n export DSTACK_APP_DOMAIN=$DSTACK_APP_ID\\\".\\\"$DSTACK_GATEWAY_DOMAIN\\nfi\\n\\necho \\\"----------------------------------------------\\\"\\necho \\\"Script execution completed\\\"\\necho \\\"----------------------------------------------\\\"\\n\",\"public_logs\":true,\"public_sysinfo\":true,\"public_tcbinfo\":true,\"runner\":\"docker-compose\",\"secure_time\":false,\"storage_fs\":\"zfs\",\"tproxy_enabled\":true}", "key_custody": {"provider": "dstack-kms", "keys": [{"role": "receipt", "path": "aci/receipt-ed25519/v1", "purpose": "aci.receipt.ed25519.v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a", "kms_public_key": "02f7fb3ed8c352d316dfdc19458a9f29c06eb7e74130b3808d784e2fd401a2c12d", "signature_chain": ["d1f59335effc2688acdf502ee34d8d3373b0e7e6be493075383714ce5673a29973f224690437c483ae3fdd37172df3abad82877c4023ed7c5a64d5acaf18477c00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-secp256k1", "path": "aci/e2ee/v1", "purpose": "aci.e2ee.v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d", "kms_public_key": "03943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f", "signature_chain": ["452d6f86a807fef0ce0d65e6c5cd25a0a249b1fd8608d26c3efe17fc10346f4c2d3dfb8969d10dc1feac1d595b3816f63faa40ac370a3a233ce1d712d418c89701", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-x25519", "path": "aci/e2ee-x25519/v1", "purpose": "aci.e2ee.x25519.v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537", "kms_public_key": "03892f86b3f55f78cc0760cec933cf8e2a92862fb173c2d221cc25895847cbb5eb", "signature_chain": ["358cd661ca4f5e986ed950ca083bf7bcfef6ab4b839ca11a953b156d64f82b1c51a213730d5777b4c845170827680d41e8220c2f950a38081025ff16eb3789ba00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}]}, "downstream_tls_binding": {"domain": "tee.redpill.ai", "spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b"}}}, "service_capabilities": {"supported_e2ee_versions": ["2"], "serving": "aggregator"}}}
\ No newline at end of file
+{"captured_at": "2026-09-14", "endpoint": "https://tee.redpill.ai/v1/aci/attestation", "nonce": "74823bb0e609a4595fe7e6b166e23522d9da5fddec467ede0f1e1c4508de6856", "note": "app_compose is held base64-encoded: the measured compose is a shell script full of paths, and the repository secret scanner reads those as leaked local paths. The test decodes it before hashing.", "report": {"api_version": "aci/1", "workload_keyset_digest": "sha256:9223dbe01170f56efeae9a72b0df74eeb3b30befecb3ec24d9c11413d1a25510", "attestation": {"tee_type": "tdx", "workload_keyset": {"subject": null, "not_after": 1791969683, "receipt_signing_keys": [{"key_id": "dstack-kms-receipt-ed25519-v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a"}], "e2ee_public_keys": [{"key_id": "dstack-kms-e2ee-v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d"}, {"key_id": "dstack-kms-e2ee-x25519-v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537"}], "tls_public_keys": [{"spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b", "domain": "tee.redpill.ai"}, {"spki_sha256": "8bafb8e6ddc55e3e662b6385a2820494fc6e90c64688136860b1851a81b7ee06", "domain": "inference.phala.com"}, {"spki_sha256": "c68585e7dc00a55d3082a9c860cb916e1788c5daec51505e413d27ab94f7d4c7", "domain": "api.redpill.ai"}, {"spki_sha256": "29a17c8e20ffed55893f6d729a75a7cf34637961d268e870842d1f2f7badd7e5", "domain": "openrouter.phala.com"}]}, "report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc2", "source_provenance": {"repo_url": "https://github.com/Dstack-TEE/private-ai-gateway.git", "repo_commit": "3e56bd30dd459d0df90afeb6d63eca7a919bc22f", "image_digest": null, "image_provenance": null}, "evidence": {"quote": "040002008100000000000000939a7233f79c4ca9940a0db3957f06074d79685452a1c5725d6ff7f9d09c91b4000000000b0105000000000000000000000000007bf063280e94fb051f5dd7b1fc59ce9aac42bb961df8d44b709c9b0ff87a7b4df648657ba6d1189589feab1d5a3c9a9d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000702000000000000f06dfda6dce1cf904d4e2bab1dc370634cf95cefa2ceb2de2eee127c9382698090d7a4a13e14c536ec6c9c3c8fa87077011a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046a83f6bbbeaa274aaf04861ef47d7e8898d4e699126b642f72f582e8d7f5349cb266a188bbabf619940d4fece06594b07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878cdf67e467e60edc1737bcf8e682d48131bfb427f523226aa7f197a7608e9b3784783fa759ef5b28191fa12f9ddb36b85819d97a9f6d54660386dddd1892775007e47134c704e91b2654b056fb2174fa5d547fb0d76bf67e75aaafdd641b182e1367ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000cc100000a7a18da0806034774b872a4fcf4dee8ee566b6af0a55f83acbfc76ba5befe2c5108b444149b24e6520ab118a982f953ac52c809f9dce9ee47a8cebbf53fa4916f0bfa28939a048d1d67a664ae6595ec4514829a10a01f7fc10230dd75c44697bf80c2e1e89a20ca66c1f76795d1cf5553a394c311bcc4601b9231b111d70d6b506004610000005050a0a05ff00020000000000000000000000000000000000000000000000000000000000000000000000000000000015000000000000000700000000000000e5a3a7b5d830c2953b98534c6c59a3a34fdc34e933f7f5898f0a85cf08846bca0000000000000000000000000000000000000000000000000000000000000000dc9e2a7c6f948f17474e34a7fc43ed030f7c1563f1babddf6340c82e0e54a8c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c1dc25ab6fd4ae8afb815c1c46f6d3f9bbe7ab053e699d259bf9136751037b20000000000000000000000000000000000000000000000000000000000000000a83b9d22a44e8d08fb7c247a64a52059f3b6b832995038b1a3b6578b0fc6fd488012210fe4a3762e3019de96ee5eb63bf1977043834cab40034ee81d35efd4082000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f05005e0e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d49494538544343424a656741774942416749555359617753595541735374766a483236625541434d506e6f6a797777436759494b6f5a497a6a3045417749770a634445694d434147413155454177775a535735305a577767553064594946424453794251624746305a6d397962534244515445614d42674741315545436777520a535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d51737743515944565151490a44414a445154454c4d416b474131554542684d4356564d774868634e4d6a59774d7a49784d4463304e5455795768634e4d7a4d774d7a49784d4463304e5455790a576a42774d534977494159445651514444426c4a626e526c624342545231676755454e4c49454e6c636e52705a6d6c6a5958526c4d526f77474159445651514b0a4442464a626e526c6243424462334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e560a4241674d416b4e424d517377435159445651514745774a56557a425a4d424d4742797147534d34394167454743437147534d34394177454841304941424747690a41664533764c54435150696e5470685047695854516c645948654f6f657257516d4b66745141787939446c63353469446b4b537852786e7a38474f7a466f65690a6b584e532b44374575464864666b476a754e796a67674d4e4d4949444354416642674e5648534d4547444157674253566231334e765276683655424a796454300a4d383442567776655644427242674e56485238455a4442694d47436758714263686c706f64485277637a6f764c32467761533530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c334e6e6543396a5a584a3061575a7059324630615739754c3359304c33426a61324e796244396a595431770a624746305a6d397962535a6c626d4e765a476c755a7a316b5a584977485159445652304f42425945464c6d495a7555304e42506c79336e757041556f41396f590a73625a514d41344741315564447745422f775145417749477744414d42674e5648524d4241663845416a41414d4949434f67594a4b6f5a496876684e415130420a424949434b7a4343416963774867594b4b6f5a496876684e415130424151515162536e396a546e656c37306e456e776e523951666554434341575147436971470a534962345451454e41514977676746554d42414743797147534962345451454e41514942416745464d42414743797147534962345451454e41514943416745460a4d42414743797147534962345451454e41514944416745434d42414743797147534962345451454e41514945416745434d42414743797147534962345451454e0a41514946416745464d42454743797147534962345451454e41514947416749412f7a415142677371686b69472b453042445145434277494241444151426773710a686b69472b4530424451454343414942416a415142677371686b69472b45304244514543435149424144415142677371686b69472b45304244514543436749420a4144415142677371686b69472b45304244514543437749424144415142677371686b69472b45304244514543444149424144415142677371686b69472b4530420a44514543445149424144415142677371686b69472b45304244514543446749424144415142677371686b69472b453042445145434477494241444151426773710a686b69472b45304244514543454149424144415142677371686b69472b45304244514543455149424454416642677371686b69472b45304244514543456751510a425155434167582f4141494141414141414141414144415142676f71686b69472b45304244514544424149414144415542676f71686b69472b453042445145450a424159676f473841414141774477594b4b6f5a496876684e4151304242516f424154416542676f71686b69472b453042445145474242414e78727a30753075620a48784e354e3964353631576d4d45514743697147534962345451454e415163774e6a415142677371686b69472b45304244514548415145422f7a4151426773710a686b69472b45304244514548416745422f7a415142677371686b69472b45304244514548417745422f7a414b42676771686b6a4f5051514441674e49414442460a4169454131305850553751764338564a743949654f724a366c4430744b58462b54746a4e422f6f7a62635235517477434944576e57773946484e7351745359420a70394c5463792f5670376f517367417076763332732f4236576b73760a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "quote_report_data": "67ca10d6bbd2c38b006f26f1dd4db0d7a84cf9fa44e81efc6b277bf7e19edcc20000000000000000000000000000000000000000000000000000000000000000", "event_log": "[{\"imr\":0,\"event_type\":2147483659,\"digest\":\"5fe4f9ee465a361d077a6892f6bc4d19a82b272fea2e3c2c1ef9c33b4d9050115fca0c6dc7a7f68f91c623de38c48226\",\"event\":\"\",\"event_payload\":\"095464785461626c65000100000000000000af96bb93f2b9b84e9462e0ba745642360090800000000000\"},{\"imr\":0,\"event_type\":2147483658,\"digest\":\"344bc51c980ba621aaa00da3ed7436f7d6e549197dfe699515dfa2c6583d95e6412af21c097d473155875ffd561d6790\",\"event\":\"\",\"event_payload\":\"2946762858585858585858582d585858582d585858582d585858582d58585858585858585858585829000000c0ff000000000040080000000000\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"9dc3a1f80bcec915391dcda5ffbb15e7419f77eab462bbf72b42166fb70d50325e37b36f93537a863769bcf9bedae6fb\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0a00000000000000000000000000000053006500630075007200650042006f006f007400\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"6f2e3cbc14f9def86980f5f66fd85e99d63e69a73014ed8a5633ce56eca5b64b692108c56110e22acadcef58c3250f1b\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0200000000000000000000000000000050004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"d607c0efb41c0d757d69bca0615c3a9ac0b1db06c557d992e906c6b7dee40e0e031640c7bfd7bcd35844ef9edeadc6f9\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c030000000000000000000000000000004b0045004b00\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"08a74f8963b337acb6c93682f934496373679dd26af1089cb4eaf0c30cf260a12e814856385ab8843e56a9acea19e127\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f0200000000000000000000000000000064006200\"},{\"imr\":0,\"event_type\":2147483649,\"digest\":\"18cc6e01f0c6ea99aa23f8a280423e94ad81d96d0aeb5180504fc0f7a40cb3619dd39bd6a95ec1680a86ed6ab0f9828d\",\"event\":\"\",\"event_payload\":\"cbb219d73a3d9645a3bcdad00e67656f03000000000000000000000000000000640062007800\"},{\"imr\":0,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":0,\"event_type\":10,\"digest\":\"febfba94a148ef74278a30ed690856a468636b51f619d44025c4f9dc0212068369895d35c67157ccd2149cd049f3761c\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"5427470ec5de45a3ad085101e7567c7f0b4aa4b0147d9aa583f067fdcb15155fc52a22bd23fd8ea10d045751f8e52337\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":0,\"event_type\":10,\"digest\":\"2194a807023a3ce948e26538252c046bce7c76293a3750ac22a41d1c2a3584bd53cc95bbdbde2d0c9f3a906f2663840f\",\"event\":\"\",\"event_payload\":\"414350492044415441\"},{\"imr\":1,\"event_type\":2147483651,\"digest\":\"90e278aa4d688f4f05bf76f1786fea174abcea0d6b44e81cea5b3327f3f211e5c465a9c34b513283bed2512bca6d4a19\",\"event\":\"\",\"event_payload\":\"1860fd7a0000000000c485000000000000000000000000002a000000000000000403140072f728144ab61e44b8c39ebdd7f893c7040412006b00650072006e0065006c0000007fff0400\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"1dd6f7b457ad880d840d41c961283bab688e94e4b59359ea45686581e90feccea3c624b1226113f824f315eb60ae0a7c\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c0900000000000000020000000000000042006f006f0074004f0072006400650072000000\"},{\"imr\":0,\"event_type\":2147483650,\"digest\":\"23ada07f5261f12f34a0bd8e46760962d6b4d576a416f1fea1c64bc656b1d28eacf7047ae6e967c58fd2a98bfa74c298\",\"event\":\"\",\"event_payload\":\"61dfe48bca93d211aa0d00e098032b8c08000000000000003e0000000000000042006f006f0074003000300030003000090100002c0055006900410070007000000004071400c9bdb87cebf8344faaea3ee4af6516a10406140021aa2c4614760345836e8ab6f46623317fff0400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"77a0dab2312b4e1e57a84d865a21e5b2ee8d677a21012ada819d0a98988078d3d740f6346bfe0abaa938ca20439a8d71\",\"event\":\"\",\"event_payload\":\"43616c6c696e6720454649204170706c69636174696f6e2066726f6d20426f6f74204f7074696f6e\"},{\"imr\":1,\"event_type\":4,\"digest\":\"394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0\",\"event\":\"\",\"event_payload\":\"00000000\"},{\"imr\":2,\"event_type\":6,\"digest\":\"9c824b958dbbef07ec732fd79727e995d5eca309dbc6d62e4fd2840f267c5e87108b5fe25c7997c67801ff391fb67ffc\",\"event\":\"\",\"event_payload\":\"ed223b8f1a0000004c4f414445445f494d4147453a3a4c6f61644f7074696f6e7300\"},{\"imr\":2,\"event_type\":6,\"digest\":\"5eb4f34f2ec49071551c5562ebaab9f86408f4a0eadaa3d7f05157e37ab04a1f040a82eaae94015d628a7ea4aa482cd2\",\"event\":\"\",\"event_payload\":\"ec223b8f0d0000004c696e757820696e6974726400\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"214b0bef1379756011344877743fdc2a5382bac6e70362d624ccf3f654407c1b4badf7d8f9295dd3dabdef65b27677e0\",\"event\":\"\",\"event_payload\":\"4578697420426f6f7420536572766963657320496e766f636174696f6e\"},{\"imr\":1,\"event_type\":2147483655,\"digest\":\"0a2e01c85deae718a530ad8c6d20a84009babe6c8989269e950d8cf440c6e997695e64d455c4174a652cd080f6230b74\",\"event\":\"\",\"event_payload\":\"4578697420426f6f742053657276696365732052657475726e656420776974682053756363657373\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"f9974020ef507068183313d0ca808e0d1ca9b2d1ad0c61f5784e7157c362c06536f5ddacdad4451693f48fcc72fff624\",\"event\":\"system-preparing\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"8562fc3980258ea5119a31356844547c42ba696d73f6ecdc7d29732beef0083f22674ce14349a8a1400254466e851407\",\"event\":\"app-id\",\"event_payload\":\"fdb7a14e5a6675f752e2cb69c9067a98ca402918\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"5edb4d5fbf301c47c77faa3de6edbcb97b93521b7c63ac6a8d8028d2f1891bd8517d76285f6e724cf28451acaac6c89e\",\"event\":\"compose-hash\",\"event_payload\":\"1a23b6cc9d265648b3feb11a2a23453e84cf847953fb406b4b4bfd368083567c\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"dbb7d7c2c2ce19938fb5989949f2c4bba6829ced5cd7b8c0d98696c293ac1c5dc8b1bfb9bc95b61e2ff778a3686651eb\",\"event\":\"instance-id\",\"event_payload\":\"15e847e02a22706121de347498427469d2e7c379\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"98bd7e6bd3952720b65027fd494834045d06b4a714bf737a06b874638b3ea00ff402f7f583e3e3b05e921c8570433ac6\",\"event\":\"boot-mr-done\",\"event_payload\":\"\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"65828c6f922910fd0e272501a8b2bb6b03e24c99bb19d1cd530586061d8738f9dd0564752db23d556a6b381cbf465a85\",\"event\":\"mr-kms\",\"event_payload\":\"692afc6d7a86a32cfc1ebd9cad1a576aab012bab46986ba609bc8d6407270572\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"c936f709860155a455c519b505fe29bde21d1b778534a8f0b41b706b0f789eae2ea899e224fedb2331c18324aab4db93\",\"event\":\"os-image-hash\",\"event_payload\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"83368b43a0fc6f824f5a9220592df85fd30e2d405ecbd253a5c6354af63e6c9b41aec557c38a38e348ab87f9ac8fc68c\",\"event\":\"key-provider\",\"event_payload\":\"7b226e616d65223a226b6d73222c226964223a223330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030343838343465623432636364663863353266643466313734663336326663623962626431396334356664343866316564656332643866316361323335333665633161373430323162346365653631306330373466383239346434333162326237666565326333396535333333666461663061343532326434336662313539643966227d\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"ba51104636900268b0e059fa3d266419d079d1e94aea26fb9fcbb8d764bf4c89a67ac271b8a0d1a3989945132a111fc7\",\"event\":\"storage-fs\",\"event_payload\":\"7a6673\"},{\"imr\":3,\"event_type\":134217729,\"digest\":\"1a76b2a80a0be71eae59f80945d876351a7a3fb8e9fd1ff1cede5734aa84ea11fd72b4edfbb6f04e5a85edd114c751bd\",\"event\":\"system-ready\",\"event_payload\":\"\"}]", "vm_config": "{\"os_image_hash\":\"bd369a8c2f9edb2b52dad48ac8e0b32dde5f1337c423a506b48d07403a7d8033\",\"cpu_count\":32,\"memory_size\":34359738368,\"qemu_version\":\"8.2.2\",\"pci_hole64_size\":0,\"hugepages\":false,\"num_gpus\":0,\"num_nvswitches\":0,\"hotplug_off\":false,\"image\":\"dstack-0.5.9-bd369a8c\",\"host_share_mode\":\"9p\",\"spec_version\":1}", "key_custody": {"provider": "dstack-kms", "keys": [{"role": "receipt", "path": "aci/receipt-ed25519/v1", "purpose": "aci.receipt.ed25519.v1", "algo": "ed25519", "public_key": "110f3824daeeef29dbeba9677a5794ffed257b1082d9fd4599b3c0819953aa0a", "kms_public_key": "02f7fb3ed8c352d316dfdc19458a9f29c06eb7e74130b3808d784e2fd401a2c12d", "signature_chain": ["d1f59335effc2688acdf502ee34d8d3373b0e7e6be493075383714ce5673a29973f224690437c483ae3fdd37172df3abad82877c4023ed7c5a64d5acaf18477c00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-secp256k1", "path": "aci/e2ee/v1", "purpose": "aci.e2ee.v1", "algo": "secp256k1-aes-256-gcm-hkdf-sha256", "public_key": "04943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f42c85b704aceaa0a396cf26f7fdf79d83205be7e11d3a5d70419a68277158c3d", "kms_public_key": "03943cea0b4babf60f6e2031e9a00866a37c4ae696fb45895b3a9b38ab8cbb898f", "signature_chain": ["452d6f86a807fef0ce0d65e6c5cd25a0a249b1fd8608d26c3efe17fc10346f4c2d3dfb8969d10dc1feac1d595b3816f63faa40ac370a3a233ce1d712d418c89701", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}, {"role": "e2ee-x25519", "path": "aci/e2ee-x25519/v1", "purpose": "aci.e2ee.x25519.v1", "algo": "x25519-aes-256-gcm-hkdf-sha256", "public_key": "408e82d159832cfae15007781bcade0badee7d50e9f76d50eaa1777464006537", "kms_public_key": "03892f86b3f55f78cc0760cec933cf8e2a92862fb173c2d221cc25895847cbb5eb", "signature_chain": ["358cd661ca4f5e986ed950ca083bf7bcfef6ab4b839ca11a953b156d64f82b1c51a213730d5777b4c845170827680d41e8220c2f950a38081025ff16eb3789ba00", "5c2fe36bdb1e434c0a371579bcbce07c4e7b89eba45affa089191baec84f23ad77b24a594aa28427b6b7a5f348b0ecf5597abfd9a2add05ddb55c521ba38a38501"]}]}, "downstream_tls_binding": {"domain": "tee.redpill.ai", "spki_sha256": "22ad6f73dd10547af55d06825c8015413968a439b8f76adc3a6aba9a32e6c71b"}, "app_compose_b64": "eyJhbGxvd2VkX2VudnMiOlsiUFJJVkFURV9BSV9HQVRFV0FZX0FETUlOX1RPS0VOIiwiUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVVJMIiwiUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVE9LRU4iLCJEU1RBQ0tfUk9PVF9QVUJMSUNfS0VZIiwiQ0xPVURGTEFSRV9BUElfVE9LRU4iLCJDRVJUQk9UX0VNQUlMIiwiTk9ERV9FWFBPUlRFUl9VU0VSIiwiTk9ERV9FWFBPUlRFUl9QQVNTV09SRF9IQVNIIiwiUFJJVkFURV9BSV9HQVRFV0FZX1BVTExfVE9LRU4iXSwiZG9ja2VyX2NvbXBvc2VfZmlsZSI6IiMgZHN0YWNrIC8gUGhhbGEgZGVwbG95bWVudCBtYW5pZmVzdCBmb3IgdGhlIHByaXZhdGUtYWktZ2F0ZXdheSBzdGFjayB2aWEgdGhlIGxhdW5jaGVyLlxuI1xuIyBUaGUgbGF1bmNoZXIgaW1hZ2UgaXMgdGhlIG1lYXN1cmVkIHRydXN0LXJvb3Q6IHBpbiBpdCBieSBAc2hhMjU2IGRpZ2VzdCAodGhlXG4jIGRpZ2VzdCArIHRoaXMgZmlsZSBhcmUgaGFzaGVkIGludG8gdGhlIFREWCBxdW90ZSkuIFRoZSBsYXVuY2hlciBjbG9uZXMrYnVpbGRzXG4jIHRoZSBnYXRld2F5IGZyb20gc291cmNlIGF0IGJvb3Q7IHRoZSBhdHRlc3RhdGlvbiB2ZXJpZmllciBpc1xuIyB0aGUgZ2F0ZXdheSdzIHZlbmRvcmVkIGNvbmZpZGVudGlhbF92ZXJpZmllciBwYWNrYWdlLCBzbyBpdCBuZWVkcyBubyBzZXBhcmF0ZSBwaW4uXG4jXG4jIFRoZSBnYXRld2F5J3Mgc291cmNlIHBpbiArIHN0YXRpYyBjb25maWcgYXJlIGlubGluZWQgYXMgY29tcG9zZSBgY29uZmlnczpgXG4jIChtZWFzdXJlZCBpbnRvIHRoZSBhdHRlc3RhdGlvbik6IHRoZSBsYXVuY2hlciBjbG9uZXMgdGhlIHBpbiBhbmQgdGhlIGdhdGV3YXlcbiMgcmVhZHMgdGhlIGNvbmZpZyBmcm9tIG1vdW50ZWQgZmlsZXMuIFNlY3JldCB2YWx1ZXMgYXJlIHN1cHBsaWVkIGFzIGRzdGFja1xuIyBFTkNSWVBURUQgU0VDUkVUUyBhbmQgcmVmZXJlbmNlZCBhcyAke1ZBUn0gc28gZHN0YWNrIGluamVjdHMgdGhlbSDigJQgb25seSB0aGVcbiMgdmFyaWFibGUgbmFtZXMgYXBwZWFyIGluIHRoaXMgcHVibGljIGZpbGUsIG5ldmVyIHRoZSB2YWx1ZXMuXG5cbnNlcnZpY2VzOlxuICAjIFRMUy10ZXJtaW5hdGluZyBpbmdyZXNzOiByZXF1ZXN0cyBDQUEtcGlubmVkIExldCdzIEVuY3J5cHQgY2VydHMgdmlhIENsb3VkZmxhcmVcbiAgIyBETlMtMDEsIHJlZ2lzdGVycyB1bmRlciB0aGUgZHN0YWNrIGdhdGV3YXkgZG9tYWluLCBhbmQgcmV2ZXJzZS1wcm94aWVzIGluYm91bmRcbiAgIyA0NDMgdG8gdGhlIGxhdW5jaGVyJ3MgZ2F0ZXdheSBvbiA6ODA4Ni4gUGluIGJ5IEBzaGEyNTYgZGlnZXN0IGxpa2UgdGhlIGxhdW5jaGVyLlxuICBkc3RhY2staW5ncmVzczpcbiAgICBpbWFnZTogZHN0YWNrdGVlL2RzdGFjay1pbmdyZXNzOjIuMkBzaGEyNTY6ZDA1YTdiMzQzYzM3YzFjY2ExYmJhOGRiZjdlOGYzYzZkMjExODE1OGFmMmQ0MWM0NTUxMDM3OTZkYjRmNjdmMFxuICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkXG4gICAgIyBUaGUgaW5ncmVzcyBpcyBhbiBMNCBUQ1AgcHJveHk6IGV2ZXJ5IGNsaWVudCBjb25uZWN0aW9uIG1hcHMgMToxIHRvIGFcbiAgICAjIGJhY2tlbmQgY29ubmVjdGlvbiB0byB0aGUgZ2F0ZXdheSwgc28gaXQgaG9sZHMgMiBmZHMgcGVyIGxpdmUgY29ubmVjdGlvbi5cbiAgICAjIGhhcHJveHkgaGFzIG5vIGB1bGltaXQtbmAgaW4gaXRzIGNvbmZpZzsgaXQgZGVyaXZlcyBhbiBmZCBuZWVkIGZyb20gTUFYQ09OTlxuICAgICMgYW5kIHJhaXNlcyBpdHMgT1dOIHNvZnQgbm9maWxlIGZyb20gdGhlIGNvbnRhaW5lciBoYXJkIGxpbWl0LiBQaW4gdGhlIGhhcmRcbiAgICAjIGxpbWl0IGhlcmUgKGxpa2UgYGxhdW5jaGVyYCkgc28gTUFYQ09OTiBpcyBuZXZlciBzaWxlbnRseSBjYXBwZWQgYnkgd2hhdGV2ZXJcbiAgICAjIHRoZSBkb2NrZXIgZGFlbW9uIGRlZmF1bHQgaGFwcGVucyB0byBiZS4gNjU1MzYgY29tZm9ydGFibHkgY292ZXJzXG4gICAgIyBNQVhDT05OIDE2Mzg0ICh+MzNrIGZkcykgd2l0aCBoZWFkcm9vbS5cbiAgICB1bGltaXRzOlxuICAgICAgbm9maWxlOlxuICAgICAgICBzb2Z0OiA2NTUzNlxuICAgICAgICBoYXJkOiA2NTUzNlxuICAgIHBvcnRzOlxuICAgICAgLSBcIjQ0Mzo0NDNcIlxuICAgIGVudmlyb25tZW50OlxuICAgICAgIyAtLS0gcHVibGljIGNvbmZpZzogbGl0ZXJhbHMsIG1lYXN1cmVkIGludG8gdGhlIGF0dGVzdGF0aW9uIC0tLVxuICAgICAgRE5TX1BST1ZJREVSOiBjbG91ZGZsYXJlXG4gICAgICBHQVRFV0FZX0RPTUFJTjogXy4ke0RTVEFDS19HQVRFV0FZX0RPTUFJTn1cbiAgICAgIFNFVF9DQUE6IFwidHJ1ZVwiXG4gICAgICAjIENvbmN1cnJlbnQtY29ubmVjdGlvbiBjZWlsaW5nLiBUaGUgaW1hZ2UgZGVmYXVsdCBpcyA0MDk2OyB1bmRlciByZWFsXG4gICAgICAjIHN0cmVhbWluZyBsb2FkIHRoZSBpbmdyZXNzIHNhdCBwaW5uZWQgYXQgdGhhdCBjZWlsaW5nIH44NCUgb2YgdGhlIHRpbWVcbiAgICAgICMgKGF2ZyB+MzcyMCBjb25jdXJyZW50KSwgc28gb25jZSBhIGJ1cnN0IHB1c2hlZCBwYXN0IDQwOTYgaGFwcm94eSBzdG9wcGVkXG4gICAgICAjIGFjY2VwdGluZyBhbmQgY2xpZW50cyBzYXcgYSBoYW5nLiBTdHJlYW1pbmcgTExNIHRyYWZmaWMga2VlcHMgdGhvdXNhbmRzIG9mXG4gICAgICAjIGxvbmctbGl2ZWQgY29ubmVjdGlvbnMgb3BlbiBhdCBvbmNlLCBzbyBnaXZlIGEgNHggaGVhZHJvb20uIE11c3Qgc3RheSB3ZWxsXG4gICAgICAjIHVuZGVyIHRoZSBub2ZpbGUgaGFyZCBsaW1pdCBhYm92ZSAoMiBmZHMvY29ubiBpbiBUQ1AgbW9kZSkuXG4gICAgICBNQVhDT05OOiBcIjE2Mzg0XCJcbiAgICAgICMgSWRsZSAobm8tZGF0YSkgdGltZW91dCwgcmVzZXQgb24gYW55IGJ5dGUgaW4gZWl0aGVyIGRpcmVjdGlvbi4gVGhlIGltYWdlXG4gICAgICAjIGRlZmF1bHQgaXMgODY0MDBzICgyNGgpOiBhIGNsaWVudCB0aGF0IHN0b3BzIHJlYWRpbmcsIG9yIGFuIHVwc3RyZWFtIHRoYXRcbiAgICAgICMgZ29lcyBzaWxlbnQsIGtlZXBzIGl0cyBzbG90IGZvciBhIGZ1bGwgZGF5LCB3aGljaCBpcyBob3cgc2xvdHMgbGVha2VkXG4gICAgICAjIHRvd2FyZCB0aGUgY2VpbGluZy4gMTgwMHMgcmVhcHMgYSBnZW51aW5lbHkgaWRsZS9zdGFsbGVkIGNvbm5lY3Rpb24gd2hpbGVcbiAgICAgICMgbmV2ZXIgdG91Y2hpbmcgYSBzdHJlYW0gdGhhdCBpcyBzdGlsbCBmbG93aW5nIChldmVuIGEgc2xvdyBvbmUpLiBOT1RFOlxuICAgICAgIyB0aGlzIGlzIHRoZSBvbmUgdmFsdWUgdG8gY29uZmlybSBhZ2FpbnN0IHRoZSBsb25nZXN0IGxlZ2l0aW1hdGUgc2lsZW50XG4gICAgICAjIGdhcCBhIHJlcXVlc3QgY2FuIGhhdmUg4oCUIGUuZy4gYSByZWFzb25pbmcgbW9kZWwgdGhhdCB0aGlua3MgPjMwIG1pbiBiZWZvcmVcbiAgICAgICMgaXRzIGZpcnN0IHRva2VuIHdvdWxkIGJlIGN1dC4gUmFpc2UgaWYgc3VjaCByZXF1ZXN0cyBhcmUgZXhwZWN0ZWQuXG4gICAgICBUSU1FT1VUX0NMSUVOVDogMTgwMHNcbiAgICAgIFRJTUVPVVRfU0VSVkVSOiAxODAwc1xuICAgICAgIyBNdWx0aS1kb21haW4gVExTLiBUaGUgaW5ncmVzcyBpcyBhbiBMNCAoVENQKSBwcm94eTogaXQgdGVybWluYXRlcyBUTFMgZm9yXG4gICAgICAjIGVhY2ggaG9zdG5hbWUgYW5kIGZvcndhcmRzIHRoZSBERUNSWVBURUQgc3RyZWFtIHVudG91Y2hlZCwgc28gdGhlIGNsaWVudCdzXG4gICAgICAjIG9yaWdpbmFsIEhUVFAgSG9zdCBzdXJ2aXZlcy4gVGhlIGdhdGV3YXkgdXNlcyB0aGF0IEhvc3QgdG8gcGljayB0aGVcbiAgICAgICMgbWF0Y2hpbmcgZG93bnN0cmVhbV90bHNfYmluZGluZy4gQWxsIHB1YmxpYyBob3N0bmFtZXMgcm91dGUgYnkgU05JIHRvIHRoZVxuICAgICAgIyBzYW1lIGdhdGV3YXkgbGlzdGVuZXIgKGNvbXBvc2Ugc2VydmljZSBuYW1lLCBub3QgbG9jYWxob3N0KS4gdGVlLnJlZHBpbGwuYWlcbiAgICAgICMgYW5kIGluZmVyZW5jZS5waGFsYS5jb20gYXJlIHRoZSBURUUtb25seSBkb21haW5zIChzZWUgYHRlZV9vbmx5X2RvbWFpbnNgXG4gICAgICAjIGJlbG93KTsgYXBpLnJlZHBpbGwuYWkgYW5kIG9wZW5yb3V0ZXIucGhhbGEuY29tIGFyZSB0aGUgbm9uLVRFRSBkb21haW5zLlxuICAgICAgRE9NQUlOUzogfFxuICAgICAgICB0ZWUucmVkcGlsbC5haVxuICAgICAgICBpbmZlcmVuY2UucGhhbGEuY29tXG4gICAgICAgIGFwaS5yZWRwaWxsLmFpXG4gICAgICAgIG9wZW5yb3V0ZXIucGhhbGEuY29tXG4gICAgICBST1VUSU5HX01BUDogfFxuICAgICAgICB0ZWUucmVkcGlsbC5haT1sYXVuY2hlcjo4MDg2XG4gICAgICAgIGluZmVyZW5jZS5waGFsYS5jb209bGF1bmNoZXI6ODA4NlxuICAgICAgICBhcGkucmVkcGlsbC5haT1sYXVuY2hlcjo4MDg2XG4gICAgICAgIG9wZW5yb3V0ZXIucGhhbGEuY29tPWxhdW5jaGVyOjgwODZcbiAgICAgICMgLS0tIHNlY3JldHM6IHZhbHVlcyBjb21lIGZyb20gZHN0YWNrIEVuY3J5cHRlZCBTZWNyZXRzIChuZXZlciBjb21taXQgdmFsdWVzKSAtLS1cbiAgICAgICMgTk9URTogdGhpcyBzaW5nbGUgQ2xvdWRmbGFyZSB0b2tlbiBtdXN0IGhhdmUgRE5TLWVkaXQgKGFuZCBDQUEpIHJpZ2h0cyBvblxuICAgICAgIyBCT1RIIHRoZSByZWRwaWxsLmFpIGFuZCBwaGFsYS5jb20gem9uZXMsIG9yIGNlcnQgaXNzdWFuY2UgZm9yIG9uZSBmYWlscy5cbiAgICAgIENMT1VERkxBUkVfQVBJX1RPS0VOOiAke0NMT1VERkxBUkVfQVBJX1RPS0VOfVxuICAgICAgQ0VSVEJPVF9FTUFJTDogJHtDRVJUQk9UX0VNQUlMfVxuICAgIHZvbHVtZXM6XG4gICAgICAtIC92YXIvcnVuL2RzdGFjay5zb2NrOi92YXIvcnVuL2RzdGFjay5zb2NrXG4gICAgICAtIC92YXIvcnVuL3RhcHBkLnNvY2s6L3Zhci9ydW4vdGFwcGQuc29ja1xuICAgICAgLSBjZXJ0LWRhdGE6L2V0Yy9sZXRzZW5jcnlwdFxuICAgICAgIyBQZXItZG9tYWluIGxlYWYgY2VydHMgKGNlcnQtPGRvbWFpbj4ucGVtKSB0aGUgZ2F0ZXdheSByZWFkcyB0byBiaW5kIFNQS0kuXG4gICAgICAtIGV2aWRlbmNlczovZXZpZGVuY2VzXG5cbiAgbGF1bmNoZXI6XG4gICAgaW1hZ2U6IGdoY3IuaW8vcmVkcGlsbC1haS9wcml2YXRlLWFpLWxhdW5jaGVyQHNoYTI1NjpjMDgzZmY5ZTZhNWRkZjEwZjZjOWU5YmIxZjc0Y2M2MThkZWViZWNmZWE1MjA4YjU2M2M1NzQzOTlkYjQ2MzdjXG4gICAgcmVzdGFydDogYWx3YXlzXG4gICAgIyBUaGUgZ2F0ZXdheSBvcGVucyBtYW55IGZkcyAoaW5ib3VuZCBjb25ucywgcGVyLXVwc3RyZWFtIHBvb2xzLCBjb250cm9sLXBsYW5lXG4gICAgIyArIHZlcmlmaWVyIGNhbGxzKS4gRG9ja2VyJ3MgZGVmYXVsdCBub2ZpbGUgKG9mdGVuIDEwMjQpIGlzIGZhciB0b28gbG93OiB1bmRlclxuICAgICMgbG9hZCBpdCBoaXQgRU1GSUxFIChcIlRvbyBtYW55IG9wZW4gZmlsZXNcIiksIHdoaWNoIGtpbGxlZCB0aGUgZ2F0ZXdheSdzIGFjY2VwdFxuICAgICMgbG9vcC4gUmFpc2UgdGhlIGxpbWl0IHdlbGwgYWJvdmUgcGVhayBjb25jdXJyZW5jeS5cbiAgICB1bGltaXRzOlxuICAgICAgbm9maWxlOlxuICAgICAgICBzb2Z0OiA2NTUzNlxuICAgICAgICBoYXJkOiA2NTUzNlxuICAgICMgc3VwZXJ2aXNvcmQgc3RyZWFtcyB0aGUgZ2F0ZXdheSBsb2dzIHRvIHRoZSBjb250YWluZXIgc3Rkb3V0OyBjYXAgdGhlXG4gICAgIyBydW50aW1lIGxvZyBvbiBkaXNrIHNvIGl0IGNhbid0IGdyb3cgdW5ib3VuZGVkICh+NUdCIHRvdGFsLCByb3RhdGVkKS5cbiAgICAjIFNpemVkIGZvciB0aGUgZ2F0ZXdheSdzIGFsd2F5cy1vbiByZXF1ZXN0X291dGNvbWUgZmFpbHVyZSBsb2dnaW5nOlxuICAgICMgcm91Z2hseSBhIG1vbnRoIG9mIHJldGVudGlvbiBvbiB0aGUgcGVyc2lzdGVudCB2b2x1bWUgZm9yIGluY2lkZW50XG4gICAgIyBmb3JlbnNpY3MuXG4gICAgbG9nZ2luZzpcbiAgICAgIGRyaXZlcjoganNvbi1maWxlXG4gICAgICBvcHRpb25zOlxuICAgICAgICBtYXgtc2l6ZTogXCI1MDBtXCJcbiAgICAgICAgbWF4LWZpbGU6IFwiMTBcIlxuICAgICMgbm8gaG9zdCBwb3J0OiB0cmFmZmljIGVudGVycyB2aWEgZHN0YWNrLWluZ3Jlc3MgKDQ0MykgYW5kIHJlYWNoZXMgdGhlXG4gICAgIyBnYXRld2F5IG92ZXIgdGhlIGNvbXBvc2UgbmV0d29yayBhcyBodHRwOi8vbGF1bmNoZXI6ODA4Ni5cbiAgICBleHBvc2U6XG4gICAgICAtIFwiODA4NlwiXG4gICAgdm9sdW1lczpcbiAgICAgIC0gL3Zhci9ydW4vZHN0YWNrLnNvY2s6L3Zhci9ydW4vZHN0YWNrLnNvY2sgICAjIG9ubHkgdGhlIGdhdGV3YXkgdXNlcyB0aGlzXG4gICAgICAtIHBhbC1jYWNoZTovdmFyL2xpYi9wYWcvY2FjaGUgICAgICAgICAgICAgICAgICMgY2FyZ28vdXYgYnVpbGQgY2FjaGUgKHNwZWVkcyByZWJvb3RzKVxuICAgICAgLSBwYWwtc3RhdGU6L3Zhci9saWIvcGFnL3N0YXRlICAgICAgICAgICAgICAgICAjIG11dGFibGUgdXBzdHJlYW1zLmpzb24gKyBzZXNzaW9ucy5qc29ubFxuICAgICAgLSBldmlkZW5jZXM6L2V2aWRlbmNlczpybyAgICAgICAgICAgICAgICAgICAgICAjIGluZ3Jlc3MtbWludGVkIHBlci1kb21haW4gbGVhZiBjZXJ0cyAoY2VydC08ZG9tYWluPi5wZW0pXG4gICAgIyBTdGF0aWMgZ2F0ZXdheSBjb25maWcgKyB1cHN0cmVhbSBzZWVkIGFyZSBtb3VudGVkIGZyb20gdGhlIGBjb25maWdzOmAgYmxvY2tcbiAgICAjIGF0IHRoZSBib3R0b20gKG1lYXN1cmVkIGludG8gdGhlIGF0dGVzdGF0aW9uKS4gVGhlIHNvdXJjZSBwaW4gaXMgcGFzc2VkIHZpYVxuICAgICMgYGVudmlyb25tZW50OmAgaW5zdGVhZCAoc2VlIGJlbG93KS5cbiAgICBjb25maWdzOlxuICAgICAgLSBzb3VyY2U6IGdhdGV3YXktY29uZmlnXG4gICAgICAgIHRhcmdldDogL2V0Yy9wcml2YXRlLWFpLWdhdGV3YXkvZ2F0ZXdheS5jb25maWcuanNvblxuICAgICAgLSBzb3VyY2U6IGdhdGV3YXktdXBzdHJlYW1zXG4gICAgICAgIHRhcmdldDogL2V0Yy9wcml2YXRlLWFpLWdhdGV3YXkvdXBzdHJlYW1zLnNlZWQuanNvblxuICAgIGVudmlyb25tZW50OlxuICAgICAgIyBUaGUgZ2F0ZXdheSByZWFkcyBpdHMgd2hvbGUgcG9saWN5IGZyb20gdGhpcyBtb3VudGVkIHN0YXRpYyBjb25maWcgZmlsZVxuICAgICAgIyAoc2VlIGBjb25maWdzOmAgYWJvdmUpOyBDT05GSUdfUEFUSCBpcyB0aGUgb25seSBnYXRld2F5LWZhY2luZyBlbnYgdmFyLlxuICAgICAgLSBQUklWQVRFX0FJX0dBVEVXQVlfQ09ORklHX1BBVEg9L2V0Yy9wcml2YXRlLWFpLWdhdGV3YXkvZ2F0ZXdheS5jb25maWcuanNvblxuICAgICAgIyBUcmFjaW5nIGZpbHRlci4gYHJlcXVlc3Rfb3V0Y29tZT1kZWJ1Z2AgdHVybnMgb24gdGhlIHJhdyBlcnJvci1kZXRhaWxcbiAgICAgICMgZmllbGQgb2YgdGhlIHBlci1yZXF1ZXN0IGZhaWx1cmUgbG9nIOKAlCBhIGRlbGliZXJhdGUgb3BlcmF0b3IgY2hvaWNlOlxuICAgICAgIyB1cHN0cmVhbSBlcnJvciBib2RpZXMgbWF5IGVjaG8gcmVxdWVzdCBmcmFnbWVudHMgaW50byB0aGVzZVxuICAgICAgIyBvcGVyYXRvci12aXNpYmxlIGxvZ3MuIERyb3AgdGhlIGByZXF1ZXN0X291dGNvbWU9ZGVidWdgIHBhcnQgdG8gZmFsbFxuICAgICAgIyBiYWNrIHRvIGNvbnRlbnQtZnJlZSBmYWlsdXJlIGxvZ2dpbmcgb25seS5cbiAgICAgIC0gUlVTVF9MT0c9aW5mbyxyZXF1ZXN0X291dGNvbWU9ZGVidWdcbiAgICAgICMgR2F0ZXdheSBzb3VyY2UgcGluLiBUaGVzZSBsaXRlcmFscyBhcmUgbWVhc3VyZWQgaW50byB0aGUgYXR0ZXN0YXRpb24sIGFuZFxuICAgICAgIyBiZWNhdXNlIHRoZXkgbGl2ZSBpbiBgZW52aXJvbm1lbnQ6YCAocGFydCBvZiBDb21wb3NlJ3MgY29uZmlnLWhhc2gpIGFcbiAgICAgICMgQ09NTUlUX1NIQSBidW1wIGZvcmNlcyBhIGNvbnRhaW5lciByZWNyZWF0ZSBvbiBgcGhhbGEgZGVwbG95YCDigJQgc28gdGhlIG5ld1xuICAgICAgIyBwaW4gYWN0dWFsbHkgdGFrZXMgZWZmZWN0LiAoQSBgY29uZmlnczpgLWZpbGUgcGluIHdvdWxkIG5vdDogQ29tcG9zZSBpZ25vcmVzXG4gICAgICAjIGNvbmZpZyBjb250ZW50IHdoZW4gZGVjaWRpbmcgdG8gcmVjcmVhdGUsIHNvIHRoZSBjaGFuZ2Ugd291bGQgbmV2ZXIgYXBwbHkuKVxuICAgICAgIyBlbnRyeXBvaW50LnNoIHdyaXRlcyAvZXRjL2dpdC1sYXVuY2hlci9nYXRld2F5LmNvbmYgZnJvbSB0aGVzZSwgYW5kIHRoZVxuICAgICAgIyBnYXRld2F5IHJlYWRzIHRoYXQgZmlsZSBhcyBpdHMgYXR0ZXN0ZWQgc291cmNlIHByb3ZlbmFuY2UuXG4gICAgICAtIFBSSVZBVEVfQUlfR0FURVdBWV9SRVBPX1VSTD1odHRwczovL2dpdGh1Yi5jb20vRHN0YWNrLVRFRS9wcml2YXRlLWFpLWdhdGV3YXkuZ2l0XG4gICAgICAtIFBSSVZBVEVfQUlfR0FURVdBWV9SRVBPX0NPTU1JVD0zZTU2YmQzMGRkNDU5ZDBkZjkwYWZlYjZkNjNlY2E3YTkxOWJjMjJmXG4gICAgICAjIE5FQVIgQUkgdmVyaWZpY2F0aW9uIHBvc3RzIFREWCBxdW90ZXMgdG8gdGhlIGRzdGFjay12ZXJpZmllciBzaWRlY2FyIGJlbG93O1xuICAgICAgIyByZWFjaCBpdCBieSBjb21wb3NlIHNlcnZpY2UgbmFtZSAobG9jYWxob3N0IHdvdWxkIGJlIHRoZSBsYXVuY2hlciBpdHNlbGYpLlxuICAgICAgLSBEU1RBQ0tfVkVSSUZJRVJfVVJMPWh0dHA6Ly9kc3RhY2stdmVyaWZpZXI6ODA4MFxuICAgICAgIyAtLS0gc2VjcmV0czogdmFsdWVzIGNvbWUgZnJvbSBkc3RhY2sgRW5jcnlwdGVkIFNlY3JldHMgKG5ldmVyIGNvbW1pdCB2YWx1ZXMpIC0tLVxuICAgICAgIyBDb250cm9sLXBsYW5lIGNyZWRlbnRpYWxzLiBUaGUgZ2F0ZXdheSdzIGluLXByb2Nlc3MgbWlkZGxld2FyZSByZWFkcyB0aGVzZVxuICAgICAgIyBmcm9tIHRoZSBgbWlkZGxld2FyZWAgY29uZmlnIHNlY3Rpb24gYmVsb3cgKHJlbmRlcmVkIGZyb20gdGhlIHNhbWUgc2VjcmV0cykuXG4gICAgICAtIFBSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1VSTD0ke1BSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1VSTH1cbiAgICAgIC0gUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVE9LRU49JHtQUklWQVRFX0FJX0dBVEVXQVlfQ09OVFJPTF9UT0tFTn1cblxuICAjIFREWCBxdW90ZSB2ZXJpZmllciBmb3IgdGhlIE5FQVIgQUkgcGF0aCAocmVwcm9kdWNlcyBBQ1BJL2Jvb3QgbWVhc3VyZW1lbnRzIHZpYVxuICAjIFFFTVUpLiBUaGUgZ2F0ZXdheSByZWFjaGVzIGl0IGF0IGh0dHA6Ly9kc3RhY2stdmVyaWZpZXI6ODA4MCAoRFNUQUNLX1ZFUklGSUVSX1VSTCkuXG4gIGRzdGFjay12ZXJpZmllcjpcbiAgICBpbWFnZTogZHN0YWNrdGVlL2RzdGFjay12ZXJpZmllcjowLjYuMC1yYzNAc2hhMjU2OjBlZDYxZDYwZjZlMGQ0NGNmNDY2ZTE1ZGM2ODk0ZjM3MTUyZDg1NjdjZmE3NmJkODc4NWE5MjFiMmQ5MDZjMzhcbiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZFxuICAgIGxvZ2dpbmc6XG4gICAgICBkcml2ZXI6IGpzb24tZmlsZVxuICAgICAgb3B0aW9uczpcbiAgICAgICAgbWF4LXNpemU6IFwiNTBtXCJcbiAgICAgICAgbWF4LWZpbGU6IFwiNVwiXG5cbiAgIyBIb3N0LW1ldHJpY3MgZXhwb3J0ZXIgZm9yIG9ic2VydmluZyB0aGlzIENWTSAoQ1BVL21lbS9kaXNrL25ldC9mZCkuIEluc2lkZSBhXG4gICMgQ1ZNIHRoZSBjb250YWluZXIncyAvLCAvcHJvYywgL3N5cyBBUkUgdGhlIGd1ZXN0J3MsIHNvIG5vZGUtZXhwb3J0ZXIgcmVwb3J0c1xuICAjIHRoZSBDVk0ncyBvd24gc3RhdGUuIFBpbm5lZCBieSBAc2hhMjU2IGxpa2UgdGhlIG90aGVyIGltYWdlcyAobWVhc3VyZWQgaW50b1xuICAjIHRoZSBhdHRlc3RhdGlvbikuIEV4cG9zZWQgb24gOTEwMCAocmVhY2hlZCB2aWEgdGhlIGRzdGFjayBnYXRld2F5KSBhbmQgZ3VhcmRlZFxuICAjIGJ5IEhUVFAgYmFzaWMgYXV0aCDigJQgYm90aCB1c2VybmFtZSBhbmQgdGhlIGJjcnlwdCBwYXNzd29yZCBIQVNIIGFyZSBpbmplY3RlZCBhc1xuICAjIGRzdGFjayBFbmNyeXB0ZWQgU2VjcmV0cywgc28gbmVpdGhlciBhcHBlYXJzIGluIHRoaXMgbWVhc3VyZWQgZmlsZS5cbiAgbm9kZS1leHBvcnRlcjpcbiAgICBpbWFnZTogcHJvbS9ub2RlLWV4cG9ydGVyOnYxLjguMkBzaGEyNTY6NDAzMmM2ZDViZmQ3NTIzNDJjM2U2MzFjMmYxZGU5M2JhNmI4NmM0MWRiNmIxNjdiOWEzNTM3MmMxMzllNzcwNlxuICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkXG4gICAgcGlkOiBob3N0XG4gICAgY29tbWFuZDpcbiAgICAgIC0gJy0tcGF0aC5yb290ZnM9L2hvc3QnXG4gICAgICAtICctLXBhdGgucHJvY2ZzPS9ob3N0L3Byb2MnXG4gICAgICAtICctLXBhdGguc3lzZnM9L2hvc3Qvc3lzJ1xuICAgICAgLSAnLS13ZWIuY29uZmlnLmZpbGU9L2V0Yy9ub2RlLWV4cG9ydGVyL3dlYi1jb25maWcueW1sJ1xuICAgIHZvbHVtZXM6XG4gICAgICAtIC86L2hvc3Q6cm8scnNsYXZlXG4gICAgY29uZmlnczpcbiAgICAgIC0gc291cmNlOiBub2RlLWV4cG9ydGVyLXdlYi1jb25maWdcbiAgICAgICAgdGFyZ2V0OiAvZXRjL25vZGUtZXhwb3J0ZXIvd2ViLWNvbmZpZy55bWxcbiAgICBwb3J0czpcbiAgICAgIC0gXCI5MTAwOjkxMDBcIlxuICAgIGxvZ2dpbmc6XG4gICAgICBkcml2ZXI6IGpzb24tZmlsZVxuICAgICAgb3B0aW9uczpcbiAgICAgICAgbWF4LXNpemU6IFwiMTBtXCJcbiAgICAgICAgbWF4LWZpbGU6IFwiM1wiXG5cbnZvbHVtZXM6XG4gIHBhbC1jYWNoZTpcbiAgcGFsLXN0YXRlOlxuICBjZXJ0LWRhdGE6ICAgIyBMZXQncyBFbmNyeXB0IGNlcnRzL2tleXMgcGVyc2lzdGVkIGZvciBkc3RhY2staW5ncmVzc1xuICBldmlkZW5jZXM6ICAgIyBpbmdyZXNzIGF0dGVzdGF0aW9uIGV2aWRlbmNlICsgcGVyLWRvbWFpbiBsZWFmIGNlcnRzLCBzaGFyZWQgcmVhZC1vbmx5IHdpdGggdGhlIGdhdGV3YXlcblxuIyBJbmxpbmVkLCBtZWFzdXJlZCBpbnRvIHRoZSBhdHRlc3RhdGlvbi4gUHVibGljIHBpbnMgdXNlIExJVEVSQUwgdmFsdWVzIHNvIHRoZVxuIyBjb2RlIHZlcnNpb24gaXMgYm91bmQgaW50byBjb21wb3NlX2hhc2g7IG9ubHkgc2VjcmV0cyB1c2UgJHtWQVJ9IChpbmplY3RlZCBieVxuIyBkc3RhY2ssIG5ldmVyIG1lYXN1cmVkKS5cbmNvbmZpZ3M6XG4gICMgU3RhdGljIGdhdGV3YXkgY29uZmlnLiBgYWRtaW5fdG9rZW5gLCB0aGUgZGVkaWNhdGVkIHVwc3RyZWFtIHB1bGwgdG9rZW4sIGFuZFxuICAjIHRoZSBjb250cm9sLXBsYW5lIGNyZWRlbnRpYWxzIGFyZSBzZWNyZXRzIOKAlCAke1ZBUn0ga2VlcHMgdGhlaXIgdmFsdWVzIG91dCBvZlxuICAjIHRoZSBtZWFzdXJlZCBjb21wb3NlIHdoaWxlIHRoZSBwcmVzZW5jZSBhbmQgZGVzdGluYXRpb24gc3RheSBtZWFzdXJlZC4gVGhlXG4gICMgYG1pZGRsZXdhcmVgIHNlY3Rpb24gcnVucyB0aGUgaW4tcHJvY2VzcyBtaWRkbGV3YXJlLCB3aGljaCBjb25zdWx0cyB0aGUgY29udHJvbFxuICAjIHBsYW5lIGF0IGBjb250cm9sX3VybGAuXG4gIGdhdGV3YXktY29uZmlnOlxuICAgIGNvbnRlbnQ6IHxcbiAgICAgIHtcbiAgICAgICAgXCJiaW5kXCI6IFwiMC4wLjAuMDo4MDg2XCIsXG4gICAgICAgIFwic3RhdGVfZGlyXCI6IFwiL3Zhci9saWIvcGFnL3N0YXRlXCIsXG4gICAgICAgIFwidXBzdHJlYW1fY29uZmlnX3NlZWRfcGF0aFwiOiBcIi9ldGMvcHJpdmF0ZS1haS1nYXRld2F5L3Vwc3RyZWFtcy5zZWVkLmpzb25cIixcbiAgICAgICAgXCJ1cHN0cmVhbV9wdWxsXCI6IHtcbiAgICAgICAgICBcInVybFwiOiBcImh0dHBzOi8vc2VydmljZS5yZWRwaWxsLmFpL2FwaS9hZG1pbi9nYXRld2F5LXVwc3RyZWFtcy9jb25maWdcIixcbiAgICAgICAgICBcInRva2VuXCI6IFwiJHtQUklWQVRFX0FJX0dBVEVXQVlfUFVMTF9UT0tFTjo/c2V0IFBSSVZBVEVfQUlfR0FURVdBWV9QVUxMX1RPS0VOfVwiLFxuICAgICAgICAgIFwicmVmcmVzaF9zZWNvbmRzXCI6IDMwMCxcbiAgICAgICAgICBcInJlcXVlc3RfdGltZW91dF9zZWNvbmRzXCI6IDkwXG4gICAgICAgIH0sXG4gICAgICAgIFwiYWRtaW5fdG9rZW5cIjogXCIke1BSSVZBVEVfQUlfR0FURVdBWV9BRE1JTl9UT0tFTjo/c2V0IFBSSVZBVEVfQUlfR0FURVdBWV9BRE1JTl9UT0tFTn1cIixcbiAgICAgICAgXCJ0bHNcIjoge1xuICAgICAgICAgIFwiZG9tYWluX2NlcnRpZmljYXRlc1wiOiBbXG4gICAgICAgICAgICB7IFwiZG9tYWluXCI6IFwidGVlLnJlZHBpbGwuYWlcIiwgXCJjZXJ0aWZpY2F0ZV9wYXRoXCI6IFwiL2V2aWRlbmNlcy9jZXJ0LXRlZS5yZWRwaWxsLmFpLnBlbVwiIH0sXG4gICAgICAgICAgICB7IFwiZG9tYWluXCI6IFwiaW5mZXJlbmNlLnBoYWxhLmNvbVwiLCBcImNlcnRpZmljYXRlX3BhdGhcIjogXCIvZXZpZGVuY2VzL2NlcnQtaW5mZXJlbmNlLnBoYWxhLmNvbS5wZW1cIiB9LFxuICAgICAgICAgICAgeyBcImRvbWFpblwiOiBcImFwaS5yZWRwaWxsLmFpXCIsIFwiY2VydGlmaWNhdGVfcGF0aFwiOiBcIi9ldmlkZW5jZXMvY2VydC1hcGkucmVkcGlsbC5haS5wZW1cIiB9LFxuICAgICAgICAgICAgeyBcImRvbWFpblwiOiBcIm9wZW5yb3V0ZXIucGhhbGEuY29tXCIsIFwiY2VydGlmaWNhdGVfcGF0aFwiOiBcIi9ldmlkZW5jZXMvY2VydC1vcGVucm91dGVyLnBoYWxhLmNvbS5wZW1cIiB9XG4gICAgICAgICAgXVxuICAgICAgICB9LFxuICAgICAgICBcIm1pZGRsZXdhcmVcIjoge1xuICAgICAgICAgIFwiY29udHJvbF91cmxcIjogXCIke1BSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1VSTDo/c2V0IFBSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1VSTH1cIixcbiAgICAgICAgICBcImNvbnRyb2xfdG9rZW5cIjogXCIke1BSSVZBVEVfQUlfR0FURVdBWV9DT05UUk9MX1RPS0VOOj9zZXQgUFJJVkFURV9BSV9HQVRFV0FZX0NPTlRST0xfVE9LRU59XCIsXG4gICAgICAgICAgXCJ0ZWVfb25seV9kb21haW5zXCI6IFtcInRlZS5yZWRwaWxsLmFpXCIsIFwiaW5mZXJlbmNlLnBoYWxhLmNvbVwiXVxuICAgICAgICB9XG4gICAgICB9XG4gICMgSW5pdGlhbCB1cHN0cmVhbSByb3V0ZXM6IHNlZWRlZCBpbnRvIDxzdGF0ZV9kaXI+L3Vwc3RyZWFtcy5qc29uIG9ubHkgd2hlbiBlbXB0eSxcbiAgIyB0aGVuIG1hbmFnZWQgYXQgcnVudGltZSB2aWEgUFVUIC92MS9hZG1pbi91cHN0cmVhbXMgKGRvZXMgbm90IHRvdWNoIHRoaXMgZmlsZSkuXG4gIGdhdGV3YXktdXBzdHJlYW1zOlxuICAgIGNvbnRlbnQ6IHxcbiAgICAgIFtdXG4gICMgbm9kZS1leHBvcnRlciBiYXNpYy1hdXRoIGNyZWRlbnRpYWxzLiBleHBvcnRlci10b29sa2l0J3MgYmFzaWNfYXV0aF91c2VycyBtYXBzXG4gICMgdXNlcm5hbWUgLT4gYmNyeXB0IGhhc2g7IGJvdGggYXJlICR7VkFSfSBzbyBkc3RhY2sgc3Vic3RpdHV0ZXMgdGhlIGluamVjdGVkXG4gICMgc2VjcmV0IHZhbHVlcyB3aGVuIHJlbmRlcmluZyB0aGlzIGZpbGUgaW50byB0aGUgY29udGFpbmVyIOKAlCBvbmx5IHRoZSBsaXRlcmFsc1xuICAjIGFyZSBtZWFzdXJlZC4gTk9ERV9FWFBPUlRFUl9QQVNTV09SRF9IQVNIIG11c3QgYmUgYSBiY3J5cHQgaGFzaFxuICAjIChgaHRwYXNzd2QgLW5CQyAxMCBcIlwiIHwgdHIgLWQgJzpcXG4nYCksIE5PVCB0aGUgcGxhaW50ZXh0IHBhc3N3b3JkOyB0aGUgcGxhaW50ZXh0XG4gICMgZ29lcyB0byB0aGUgZXh0ZXJuYWwgUHJvbWV0aGV1cyBzY3JhcGUgY29uZmlnLlxuICBub2RlLWV4cG9ydGVyLXdlYi1jb25maWc6XG4gICAgY29udGVudDogfFxuICAgICAgYmFzaWNfYXV0aF91c2VyczpcbiAgICAgICAgJHtOT0RFX0VYUE9SVEVSX1VTRVI6P3NldCBOT0RFX0VYUE9SVEVSX1VTRVJ9OiAke05PREVfRVhQT1JURVJfUEFTU1dPUkRfSEFTSDo/c2V0IE5PREVfRVhQT1JURVJfUEFTU1dPUkRfSEFTSH1cbiIsImZlYXR1cmVzIjpbImttcyIsInRwcm94eS1uZXQiXSwiZ2F0ZXdheV9lbmFibGVkIjp0cnVlLCJrbXNfZW5hYmxlZCI6dHJ1ZSwibG9jYWxfa2V5X3Byb3ZpZGVyX2VuYWJsZWQiOmZhbHNlLCJtYW5pZmVzdF92ZXJzaW9uIjoyLCJuYW1lIjoiIiwibm9faW5zdGFuY2VfaWQiOmZhbHNlLCJwcmVfbGF1bmNoX3NjcmlwdCI6IiMhL2Jpbi9iYXNoXG5lY2hvIFwiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVwiXG5lY2hvIFwiUnVubmluZyBQaGFsYSBDbG91ZCBQcmUtTGF1bmNoIFNjcmlwdCB2MC4wLjE1XCJcbmVjaG8gXCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXCJcbnNldCAtZVxuXG4jIEZ1bmN0aW9uOiBub3RpZnkgaG9zdFxuXG5ub3RpZnlfaG9zdCgpIHtcbiAgICBpZiBjb21tYW5kIC12IGRzdGFjay11dGlsID4vZGV2L251bGwgMj4mMTsgdGhlblxuICAgICAgICBkc3RhY2stdXRpbCBub3RpZnktaG9zdCAtZSBcIiQxXCIgLWQgXCIkMlwiXG4gICAgZWxzZVxuICAgICAgICB0ZHhjdGwgbm90aWZ5LWhvc3QgLWUgXCIkMVwiIC1kIFwiJDJcIlxuICAgIGZpXG59XG5cbm5vdGlmeV9ob3N0X2hvb3RfaW5mbygpIHtcbiAgICBub3RpZnlfaG9zdCBcImJvb3QucHJvZ3Jlc3NcIiBcIiQxXCJcbn1cblxubm90aWZ5X2hvc3RfaG9vdF9lcnJvcigpIHtcbiAgICBub3RpZnlfaG9zdCBcImJvb3QuZXJyb3JcIiBcIiQxXCJcbn1cblxuIyBGdW5jdGlvbjogUGVyZm9ybSBEb2NrZXIgY2xlYW51cFxucGVyZm9ybV9jbGVhbnVwKCkge1xuICAgIGVjaG8gXCJQcnVuaW5nIHVudXNlZCBpbWFnZXNcIlxuICAgIGRvY2tlciBpbWFnZSBwcnVuZSAtYWZcbiAgICBlY2hvIFwiUHJ1bmluZyB1bnVzZWQgdm9sdW1lc1wiXG4gICAgZG9ja2VyIHZvbHVtZSBwcnVuZSAtZlxuICAgIG5vdGlmeV9ob3N0X2hvb3RfaW5mbyBcImRvY2tlciBjbGVhbnVwIGNvbXBsZXRlZFwiXG59XG5cbiMgRnVuY3Rpb246IENoZWNrIERvY2tlciBsb2dpbiBzdGF0dXMgd2l0aG91dCBleHBvc2luZyBjcmVkZW50aWFsc1xuY2hlY2tfZG9ja2VyX2xvZ2luKCkge1xuICAgIGxvY2FsIHJlZ2lzdHJ5PVwiJDFcIlxuXG4gICAgIyBXaGVuIHJlZ2lzdHJ5IGlzIHNwZWNpZmllZCwgY2hlY2sgYXV0aCBlbnRyeSBmb3IgdGhhdCByZWdpc3RyeSBpbiBEb2NrZXIgY29uZmlnXG4gICAgaWYgW1sgLW4gXCIkcmVnaXN0cnlcIiBdXTsgdGhlblxuICAgICAgICBsb2NhbCBkb2NrZXJfY29uZmlnX3BhdGg9XCIke0RPQ0tFUl9DT05GSUc6LSRIT01FLy5kb2NrZXJ9L2NvbmZpZy5qc29uXCJcbiAgICAgICAgaWYgW1sgLWYgXCIkZG9ja2VyX2NvbmZpZ19wYXRoXCIgXV0gJiYgZ3JlcCAtcSBcIiRyZWdpc3RyeVwiIFwiJGRvY2tlcl9jb25maWdfcGF0aFwiOyB0aGVuXG4gICAgICAgICAgICByZXR1cm4gMFxuICAgICAgICBlbHNlXG4gICAgICAgICAgICByZXR1cm4gMVxuICAgICAgICBmaVxuICAgIGZpXG5cbiAgICAjIEZhbGxiYWNrIGNoZWNrIHdoZW4gbm8gZXhwbGljaXQgcmVnaXN0cnkgaXMgcHJvdmlkZWRcbiAgICBpZiBkb2NrZXIgaW5mbyAyPi9kZXYvbnVsbCB8IGdyZXAgLXEgXCJVc2VybmFtZVwiOyB0aGVuXG4gICAgICAgIHJldHVybiAwXG4gICAgZWxzZVxuICAgICAgICByZXR1cm4gMVxuICAgIGZpXG59XG5cbiMgTWFpbiBsb2dpYyBzdGFydHMgaGVyZVxuZWNobyBcIlN0YXJ0aW5nIGxvZ2luIHByb2Nlc3MuLi5cIlxuXG4jIENoZWNrIGlmIERvY2tlciBjcmVkZW50aWFscyBleGlzdFxuaWYgW1sgLW4gXCIkRFNUQUNLX0RPQ0tFUl9VU0VSTkFNRVwiICYmIC1uIFwiJERTVEFDS19ET0NLRVJfUEFTU1dPUkRcIiBdXTsgdGhlblxuICAgIGVjaG8gXCJEb2NrZXIgY3JlZGVudGlhbHMgZm91bmRcIlxuICAgIERPQ0tFUl9SRUdJU1RSWV9UQVJHRVQ9XCIke0RTVEFDS19ET0NLRVJfUkVHSVNUUlk6LWRvY2tlci5pb31cIlxuICAgIGVjaG8gXCJUYXJnZXQgRG9ja2VyIHJlZ2lzdHJ5OiAkRE9DS0VSX1JFR0lTVFJZX1RBUkdFVFwiXG5cbiAgICAjIENoZWNrIGlmIGFscmVhZHkgbG9nZ2VkIGluXG4gICAgaWYgY2hlY2tfZG9ja2VyX2xvZ2luIFwiJERTVEFDS19ET0NLRVJfUkVHSVNUUllcIjsgdGhlblxuICAgICAgICBlY2hvIFwiQWxyZWFkeSBsb2dnZWQgaW4gdG8gRG9ja2VyIHJlZ2lzdHJ5OiAkRE9DS0VSX1JFR0lTVFJZX1RBUkdFVFwiXG4gICAgZWxzZVxuICAgICAgICBlY2hvIFwiTG9nZ2luZyBpbiB0byBEb2NrZXIgcmVnaXN0cnk6ICRET0NLRVJfUkVHSVNUUllfVEFSR0VUXCJcbiAgICAgICAgIyBMb2dpbiB3aXRob3V0IGV4cG9zaW5nIHBhc3N3b3JkIGluIHByb2Nlc3MgbGlzdFxuICAgICAgICBpZiBbWyAtbiBcIiREU1RBQ0tfRE9DS0VSX1JFR0lTVFJZXCIgXV07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCIkRFNUQUNLX0RPQ0tFUl9QQVNTV09SRFwiIHwgZG9ja2VyIGxvZ2luIC11IFwiJERTVEFDS19ET0NLRVJfVVNFUk5BTUVcIiAtLXBhc3N3b3JkLXN0ZGluIFwiJERTVEFDS19ET0NLRVJfUkVHSVNUUllcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiJERTVEFDS19ET0NLRVJfUEFTU1dPUkRcIiB8IGRvY2tlciBsb2dpbiAtdSBcIiREU1RBQ0tfRE9DS0VSX1VTRVJOQU1FXCIgLS1wYXNzd29yZC1zdGRpblxuICAgICAgICBmaVxuXG4gICAgICAgIGlmIFsgJD8gLWVxIDAgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIkRvY2tlciBsb2dpbiBzdWNjZXNzZnVsOiAkRE9DS0VSX1JFR0lTVFJZX1RBUkdFVFwiXG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGVjaG8gXCJEb2NrZXIgbG9naW4gZmFpbGVkOiAkRE9DS0VSX1JFR0lTVFJZX1RBUkdFVFwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiZG9ja2VyIGxvZ2luIGZhaWxlZFwiXG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgZmlcbiAgICBmaVxuIyBDaGVjayBpZiBBV1MgRUNSIGNyZWRlbnRpYWxzIGV4aXN0XG5lbGlmIFtbIC1uIFwiJERTVEFDS19BV1NfQUNDRVNTX0tFWV9JRFwiICYmIC1uIFwiJERTVEFDS19BV1NfU0VDUkVUX0FDQ0VTU19LRVlcIiAmJiAtbiBcIiREU1RBQ0tfQVdTX1JFR0lPTlwiICYmIC1uIFwiJERTVEFDS19BV1NfRUNSX1JFR0lTVFJZXCIgXV07IHRoZW5cbiAgICBlY2hvIFwiQVdTIEVDUiBjcmVkZW50aWFscyBmb3VuZFwiXG5cbiAgICAjIENoZWNrIGlmIEFXUyBDTEkgaXMgaW5zdGFsbGVkXG4gICAgaWYgWyAhIC1mIFwiLi9hd3MvZGlzdC9hd3NcIiBdOyB0aGVuXG4gICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfaW5mbyBcImF3c2NsaSBub3QgaW5zdGFsbGVkLCBpbnN0YWxsaW5nLi4uXCJcbiAgICAgICAgZWNobyBcIkFXUyBDTEkgbm90IGluc3RhbGxlZCwgaW5zdGFsbGluZy4uLlwiXG4gICAgICAgIGN1cmwgXCJodHRwczovL2F3c2NsaS5hbWF6b25hd3MuY29tL2F3c2NsaS1leGUtbGludXgteDg2XzY0LTIuMjQuMTQuemlwXCIgLW8gXCJhd3NjbGl2Mi56aXBcIlxuICAgICAgICBlY2hvIFwiNmZmMDMxYTI2ZGY3ZGFlYmJmYTNjY2RkYzlhZjE0NTAgYXdzY2xpdjIuemlwXCIgfCBtZDVzdW0gLWNcbiAgICAgICAgaWYgWyAkPyAtbmUgMCBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiTUQ1IGNoZWNrc3VtIGZhaWxlZFwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiYXdzY2xpIGluc3RhbGwgZmFpbGVkXCJcbiAgICAgICAgICAgIGV4aXQgMVxuICAgICAgICBmaVxuICAgICAgICB1bnppcCBhd3NjbGl2Mi56aXAgJj4gL2Rldi9udWxsXG4gICAgZWxzZVxuICAgICAgICBlY2hvIFwiQVdTIENMSSBpcyBhbHJlYWR5IGluc3RhbGxlZDogLi9hd3MvZGlzdC9hd3NcIlxuICAgIGZpXG5cbiAgICAjIFNldCBBV1MgY3JlZGVudGlhbHMgYXMgZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gICAgZXhwb3J0IEFXU19BQ0NFU1NfS0VZX0lEPVwiJERTVEFDS19BV1NfQUNDRVNTX0tFWV9JRFwiXG4gICAgZXhwb3J0IEFXU19TRUNSRVRfQUNDRVNTX0tFWT1cIiREU1RBQ0tfQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZXCJcbiAgICBleHBvcnQgQVdTX0RFRkFVTFRfUkVHSU9OPVwiJERTVEFDS19BV1NfUkVHSU9OXCJcblxuICAgICMgU2V0IHNlc3Npb24gdG9rZW4gaWYgcHJvdmlkZWQgKGZvciB0ZW1wb3JhcnkgY3JlZGVudGlhbHMpXG4gICAgaWYgW1sgLW4gXCIkRFNUQUNLX0FXU19TRVNTSU9OX1RPS0VOXCIgXV07IHRoZW5cbiAgICAgICAgZWNobyBcIkFXUyBzZXNzaW9uIHRva2VuIGZvdW5kLCB1c2luZyB0ZW1wb3JhcnkgY3JlZGVudGlhbHNcIlxuICAgICAgICBleHBvcnQgQVdTX1NFU1NJT05fVE9LRU49XCIkRFNUQUNLX0FXU19TRVNTSU9OX1RPS0VOXCJcbiAgICBmaVxuXG4gICAgIyBUZXN0IEFXUyBjcmVkZW50aWFscyBiZWZvcmUgYXR0ZW1wdGluZyBFQ1IgbG9naW5cbiAgICBlY2hvIFwiVGVzdGluZyBBV1MgY3JlZGVudGlhbHMuLi5cIlxuICAgIGlmICEgLi9hd3MvZGlzdC9hd3Mgc3RzIGdldC1jYWxsZXItaWRlbnRpdHkgJj4gL2Rldi9udWxsOyB0aGVuXG4gICAgICAgIGVjaG8gXCJBV1MgY3JlZGVudGlhbHMgdGVzdCBmYWlsZWRcIlxuICAgICAgICAjIEZvciBzZXNzaW9uIHRva2VuIGNyZWRlbnRpYWxzLCB0aGlzIG1pZ2h0IGJlIGV4cGVjdGVkIGlmIHRoZXkncmUgZXhwaXJlZFxuICAgICAgICAjIExvZyB3YXJuaW5nIGJ1dCBkb24ndCBmYWlsIHN0YXJ0dXBcbiAgICAgICAgaWYgW1sgLW4gXCIkRFNUQUNLX0FXU19TRVNTSU9OX1RPS0VOXCIgXV07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJXYXJuaW5nOiBBV1MgdGVtcG9yYXJ5IGNyZWRlbnRpYWxzIG1heSBoYXZlIGV4cGlyZWQsIGNvbnRpbnVpbmcgc3RhcnR1cFwiXG4gICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJBV1MgdGVtcG9yYXJ5IGNyZWRlbnRpYWxzIG1heSBoYXZlIGV4cGlyZWRcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiQVdTIGNyZWRlbnRpYWxzIHRlc3QgZmFpbGVkXCJcbiAgICAgICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfZXJyb3IgXCJJbnZhbGlkIEFXUyBjcmVkZW50aWFsc1wiXG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgZmlcbiAgICBlbHNlXG4gICAgICAgIGVjaG8gXCJMb2dnaW5nIGluIHRvIEFXUyBFQ1IuLi5cIlxuICAgICAgICAuL2F3cy9kaXN0L2F3cyBlY3IgZ2V0LWxvZ2luLXBhc3N3b3JkIC0tcmVnaW9uICREU1RBQ0tfQVdTX1JFR0lPTiB8IGRvY2tlciBsb2dpbiAtLXVzZXJuYW1lIEFXUyAtLXBhc3N3b3JkLXN0ZGluIFwiJERTVEFDS19BV1NfRUNSX1JFR0lTVFJZXCJcbiAgICAgICAgaWYgWyAkPyAtZXEgMCBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiQVdTIEVDUiBsb2dpbiBzdWNjZXNzZnVsXCJcbiAgICAgICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfaW5mbyBcIkFXUyBFQ1IgbG9naW4gc3VjY2Vzc2Z1bFwiXG4gICAgICAgIGVsc2VcbiAgICAgICAgICAgIGVjaG8gXCJBV1MgRUNSIGxvZ2luIGZhaWxlZFwiXG4gICAgICAgICAgICAjIEZvciBzZXNzaW9uIHRva2VuIGNyZWRlbnRpYWxzLCBkb24ndCBmYWlsIHN0YXJ0dXAgaWYgbG9naW4gZmFpbHNcbiAgICAgICAgICAgIGlmIFtbIC1uIFwiJERTVEFDS19BV1NfU0VTU0lPTl9UT0tFTlwiIF1dOyB0aGVuXG4gICAgICAgICAgICAgICAgZWNobyBcIldhcm5pbmc6IEFXUyBFQ1IgbG9naW4gZmFpbGVkIHdpdGggdGVtcG9yYXJ5IGNyZWRlbnRpYWxzLCBjb250aW51aW5nIHN0YXJ0dXBcIlxuICAgICAgICAgICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfaW5mbyBcIkFXUyBFQ1IgbG9naW4gZmFpbGVkIHdpdGggdGVtcG9yYXJ5IGNyZWRlbnRpYWxzXCJcbiAgICAgICAgICAgIGVsc2VcbiAgICAgICAgICAgICAgICBub3RpZnlfaG9zdF9ob290X2Vycm9yIFwiQVdTIEVDUiBsb2dpbiBmYWlsZWRcIlxuICAgICAgICAgICAgICAgIGV4aXQgMVxuICAgICAgICAgICAgZmlcbiAgICAgICAgZmlcbiAgICBmaVxuZmlcblxucGVyZm9ybV9jbGVhbnVwXG5cbiNcbiMgR0hDUiBpbWFnZSBwdWxsIGFjY2VzcyB2ZXJpZmljYXRpb24gKHB1cmUgSFRUUCwgbm8gZG9ja2VyIGRhZW1vbilcbiNcbmlmIFtbIFwiJERPQ0tFUl9SRUdJU1RSWV9UQVJHRVRcIiA9PSBcImdoY3IuaW9cIiAmJiAtbiBcIiREU1RBQ0tfRE9DS0VSX1VTRVJOQU1FXCIgJiYgLW4gXCIkRFNUQUNLX0RPQ0tFUl9QQVNTV09SRFwiIF1dOyB0aGVuXG4gICAgQ09NUE9TRV9JTUFHRVM9JChncmVwICdpbWFnZTonIC9kc3RhY2svZG9ja2VyLWNvbXBvc2UueWFtbCAyPi9kZXYvbnVsbCB8IGF3ayAne3ByaW50ICQyfScgfCB0ciAtZCAnXCInXCInXCIgfHwgdHJ1ZSlcbiAgICBmb3IgaW1nIGluICRDT01QT1NFX0lNQUdFUzsgZG9cbiAgICAgICAgW1sgXCIkaW1nXCIgIT0gZ2hjci5pby8qIF1dICYmIGNvbnRpbnVlXG4gICAgICAgIHJlcG89XCIke2ltZyNnaGNyLmlvL31cIjsgcmVwbz1cIiR7cmVwbyUlOip9XCJcbiAgICAgICAgdGFnPVwiJHtpbWcjIyo6fVwiOyBbWyBcIiR0YWdcIiA9PSBcIiRpbWdcIiB8fCBcIiR0YWdcIiA9PSBcIiRyZXBvXCIgXV0gJiYgdGFnPVwibGF0ZXN0XCJcbiAgICAgICAgZWNobyBcIlZlcmlmeWluZyBHSENSIHB1bGwgYWNjZXNzOiAkaW1nXCJcbiAgICAgICAgdG9rZW49JChjdXJsIC1zZiAtdSBcIiREU1RBQ0tfRE9DS0VSX1VTRVJOQU1FOiREU1RBQ0tfRE9DS0VSX1BBU1NXT1JEXCIgICAgICAgICAgICAgXCJodHRwczovL2doY3IuaW8vdG9rZW4/c2VydmljZT1naGNyLmlvJnNjb3BlPXJlcG9zaXRvcnk6JHtyZXBvfTpwdWxsXCIgfCBqcSAtciAnLnRva2VuIC8vIGVtcHR5JyB8fCB0cnVlKVxuICAgICAgICBpZiBbWyAteiBcIiR0b2tlblwiIF1dOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiRVJST1I6IEdIQ1IgdG9rZW4gZXhjaGFuZ2UgZmFpbGVkIGZvciAkaW1nXCJcbiAgICAgICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfZXJyb3IgXCJHSENSIHRva2VuIGV4Y2hhbmdlIGZhaWxlZDogJGltZ1wiXG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgZmlcbiAgICAgICAgaHR0cF9jb2RlPSQoY3VybCAtcyAtbyAvZGV2L251bGwgLXcgXCIle2h0dHBfY29kZX1cIiAtSCBcIkF1dGhvcml6YXRpb246IEJlYXJlciAkdG9rZW5cIiAgICAgICAgICAgICAtSCBcIkFjY2VwdDogYXBwbGljYXRpb24vdm5kLm9jaS5pbWFnZS5pbmRleC52MStqc29uLGFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbixhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC5saXN0LnYyK2pzb24sYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvblwiICAgICAgICAgICAgIFwiaHR0cHM6Ly9naGNyLmlvL3YyLyR7cmVwb30vbWFuaWZlc3RzLyR7dGFnfVwiKVxuICAgICAgICBpZiBbWyBcIiRodHRwX2NvZGVcIiAhPSBcIjIwMFwiIF1dOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiRVJST1I6IEdIQ1IgcHVsbCBhY2Nlc3MgZGVuaWVkIGZvciAkaW1nIChIVFRQICRodHRwX2NvZGUpXCJcbiAgICAgICAgICAgIG5vdGlmeV9ob3N0X2hvb3RfZXJyb3IgXCJHSENSIHB1bGwgYWNjZXNzIGRlbmllZDogJGltZyAoSFRUUCAkaHR0cF9jb2RlKVwiXG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgZmlcbiAgICAgICAgZWNobyBcIkdIQ1IgcHVsbCBhY2Nlc3MgT0s6ICRpbWdcIlxuICAgIGRvbmVcbmZpXG5cbiNcbiMgUHVsbCBsYXRlc3QgaW1hZ2VzIGZyb20gZG9ja2VyLWNvbXBvc2UueWFtbCBzbyBleGlzdGluZyBDVk1zIHBpY2sgdXAgbmV3IHRhZ3MuXG4jIFB1bGwgaXMgZmFpbC1zb2Z0OiBzaG9ydC1saXZlZCB0b2tlbnMgbWF5IGV4cGlyZSBiZXR3ZWVuIGxvZ2luIGFuZCBwdWxsLCBidXRcbiMgZmFsbGluZyBiYWNrIHRvIGFscmVhZHktY2FjaGVkIGltYWdlcyBpcyBwcmVmZXJhYmxlIHRvIGJsb2NraW5nIGJvb3QuXG4jXG5lY2hvIFwiSW1hZ2VzIGJlZm9yZSBwdWxsOlwiXG5kb2NrZXIgaW1hZ2VzIC0tZm9ybWF0ICd7ey5SZXBvc2l0b3J5fX06e3suVGFnfX0ge3suSUR9fSAoe3suQ3JlYXRlZFNpbmNlfX0pJ1xuXG5lY2hvIFwiUHVsbGluZyBsYXRlc3QgaW1hZ2VzIGZyb20gL2RzdGFjay9kb2NrZXItY29tcG9zZS55YW1sLi4uXCJcbmlmIGRvY2tlciBjb21wb3NlIC1mIC9kc3RhY2svZG9ja2VyLWNvbXBvc2UueWFtbCBwdWxsOyB0aGVuXG4gICAgZWNobyBcImRvY2tlciBjb21wb3NlIHB1bGwgY29tcGxldGVkXCJcbiAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJkb2NrZXIgY29tcG9zZSBwdWxsIGNvbXBsZXRlZFwiXG5lbHNlXG4gICAgZWNobyBcIldBUk5JTkc6IGRvY2tlciBjb21wb3NlIHB1bGwgZmFpbGVkOyBjb250aW51aW5nIHdpdGggZXhpc3RpbmcgaW1hZ2VzXCJcbiAgICBub3RpZnlfaG9zdF9ob290X2luZm8gXCJkb2NrZXIgY29tcG9zZSBwdWxsIGZhaWxlZDsgdXNpbmcgZXhpc3RpbmcgaW1hZ2VzXCJcbmZpXG5cbmVjaG8gXCJJbWFnZXMgYWZ0ZXIgcHVsbDpcIlxuZG9ja2VyIGltYWdlcyAtLWZvcm1hdCAne3suUmVwb3NpdG9yeX19Ont7LlRhZ319IHt7LklEfX0gKHt7LkNyZWF0ZWRTaW5jZX19KSdcblxuI1xuIyBTZXQgcm9vdCBwYXNzd29yZC5cbiNcbmVjaG8gXCJTZXR0aW5nIHJvb3QgcGFzc3dvcmQuLlwiXG5cbiMgQ2hlY2sgaWYgcGFzc3dvcmQgZmlsZXMgYXJlIHdyaXRhYmxlXG5QQVNTV0RfV1JJVEFCTEU9dHJ1ZVxuaWYgWyAhIC13IC9ldGMvcGFzc3dkIF07IHRoZW5cbiAgICBlY2hvIFwiV2FybmluZzogL2V0Yy9wYXNzd2QgaXMgcmVhZC1vbmx5XCJcbiAgICBQQVNTV0RfV1JJVEFCTEU9ZmFsc2VcbmZpXG5pZiBbICEgLXcgL2V0Yy9zaGFkb3cgXTsgdGhlblxuICAgIGVjaG8gXCJXYXJuaW5nOiAvZXRjL3NoYWRvdyBpcyByZWFkLW9ubHlcIlxuICAgIFBBU1NXRF9XUklUQUJMRT1mYWxzZVxuZmlcblxuaWYgWyBcIiRQQVNTV0RfV1JJVEFCTEVcIiA9IFwiZmFsc2VcIiBdOyB0aGVuXG4gICAgZWNobyBcIlNraXBwaW5nIHBhc3N3b3JkIHNldHVwIGR1ZSB0byByZWFkLW9ubHkgZmlsZSBzeXN0ZW1cIlxuZWxzZVxuICAgICMgQ2hlY2sgaWYgY2hwYXNzd2QgaXMgYXZhaWxhYmxlXG4gICAgaWYgY29tbWFuZCAtdiBjaHBhc3N3ZCA+L2Rldi9udWxsIDI+JjE7IHRoZW5cbiAgICAgICAgZWNobyBcIlVzaW5nIGNocGFzc3dkIG1ldGhvZFwiXG5cbiAgICAgICAgaWYgWyAtbiBcIiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIF07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJTZXR0aW5nIHJvb3QgcGFzc3dvcmQgZnJvbSB1c2VyLi5cIlxuICAgICAgICAgICAgZWNobyBcInJvb3Q6JERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBjaHBhc3N3ZFxuICAgICAgICAgICAgdW5zZXQgRFNUQUNLX1JPT1RfUEFTU1dPUkRcbiAgICAgICAgICAgIGVjaG8gXCJSb290IHBhc3N3b3JkIHNldC91cGRhdGVkIGZyb20gRFNUQUNLX1JPT1RfUEFTU1dPUkRcIlxuICAgICAgICBlbGlmIFsgLXogXCIkKGdyZXAgJ15yb290OicgL2V0Yy9zaGFkb3cgMj4vZGV2L251bGwgfCBjdXQgLWQ6IC1mMilcIiBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiU2V0dGluZyByYW5kb20gcm9vdCBwYXNzd29yZC4uXCJcbiAgICAgICAgICAgIERTVEFDS19ST09UX1BBU1NXT1JEPSQoXG4gICAgICAgICAgICAgICAgTENfQUxMPUMgdHIgLWRjICdBLVphLXowLTknIDwgL2Rldi91cmFuZG9tIHwgZGQgYnM9MSBjb3VudD0zMiAyPi9kZXYvbnVsbFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgZWNobyBcInJvb3Q6JERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBjaHBhc3N3ZFxuICAgICAgICAgICAgdW5zZXQgRFNUQUNLX1JPT1RfUEFTU1dPUkRcbiAgICAgICAgICAgIGVjaG8gXCJSb290IHBhc3N3b3JkIHNldCAocmFuZG9tIGF1dG8taW5pdClcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiUm9vdCBwYXNzd29yZCBhbHJlYWR5IHNldDsgbm8gY2hhbmdlcy5cIlxuICAgICAgICBmaVxuICAgIGVsc2VcbiAgICAgICAgZWNobyBcIlVzaW5nIHBhc3N3ZCBtZXRob2RcIlxuXG4gICAgICAgIGlmIFsgLW4gXCIkRFNUQUNLX1JPT1RfUEFTU1dPUkRcIiBdOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiU2V0dGluZyByb290IHBhc3N3b3JkIGZyb20gdXNlci4uXCJcbiAgICAgICAgICAgIGVjaG8gXCIkRFNUQUNLX1JPT1RfUEFTU1dPUkRcIiB8IHBhc3N3ZCAtLXN0ZGluIHJvb3QgMj4vZGV2L251bGwgICAgICAgICAgICAgICAgIHx8IHByaW50ZiAnJXNcbiVzXG4nIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgXCIkRFNUQUNLX1JPT1RfUEFTU1dPUkRcIiB8IHBhc3N3ZCByb290XG4gICAgICAgICAgICB1bnNldCBEU1RBQ0tfUk9PVF9QQVNTV09SRFxuICAgICAgICAgICAgZWNobyBcIlJvb3QgcGFzc3dvcmQgc2V0L3VwZGF0ZWQgZnJvbSBEU1RBQ0tfUk9PVF9QQVNTV09SRFwiXG4gICAgICAgIGVsaWYgWyAteiBcIiQoZ3JlcCAnXnJvb3Q6JyAvZXRjL3NoYWRvdyAyPi9kZXYvbnVsbCB8IGN1dCAtZDogLWYyKVwiIF07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJTZXR0aW5nIHJhbmRvbSByb290IHBhc3N3b3JkLi5cIlxuICAgICAgICAgICAgRFNUQUNLX1JPT1RfUEFTU1dPUkQ9JChcbiAgICAgICAgICAgICAgICBMQ19BTEw9QyB0ciAtZGMgJ0EtWmEtejAtOScgPCAvZGV2L3VyYW5kb20gfCBkZCBicz0xIGNvdW50PTMyIDI+L2Rldi9udWxsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBlY2hvIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBwYXNzd2QgLS1zdGRpbiByb290IDI+L2Rldi9udWxsICAgICAgICAgICAgICAgICB8fCBwcmludGYgJyVzXG4lc1xuJyBcIiREU1RBQ0tfUk9PVF9QQVNTV09SRFwiIFwiJERTVEFDS19ST09UX1BBU1NXT1JEXCIgfCBwYXNzd2Qgcm9vdFxuICAgICAgICAgICAgdW5zZXQgRFNUQUNLX1JPT1RfUEFTU1dPUkRcbiAgICAgICAgICAgIGVjaG8gXCJSb290IHBhc3N3b3JkIHNldCAocmFuZG9tIGF1dG8taW5pdClcIlxuICAgICAgICBlbHNlXG4gICAgICAgICAgICBlY2hvIFwiUm9vdCBwYXNzd29yZCBhbHJlYWR5IHNldDsgbm8gY2hhbmdlcy5cIlxuICAgICAgICBmaVxuICAgIGZpXG5maVxuXG4jXG4jIFNldCBTU0ggYXV0aG9yaXplZCBrZXlzXG4jXG5pZiBta2RpciAtcCAvaG9tZS9yb290Ly5zc2ggMj4vZGV2L251bGw7IHRoZW5cbiAgICBpZiBbWyAtbiBcIiREU1RBQ0tfUk9PVF9QVUJMSUNfS0VZXCIgXV07IHRoZW5cbiAgICAgICAgZWNobyBcIiREU1RBQ0tfUk9PVF9QVUJMSUNfS0VZXCIgPiAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzXG4gICAgICAgIHVuc2V0ICREU1RBQ0tfUk9PVF9QVUJMSUNfS0VZXG4gICAgICAgIGVjaG8gXCJSb290IHB1YmxpYyBrZXkgc2V0XCJcbiAgICBmaVxuICAgIGlmIFtbIC1uIFwiJERTVEFDS19BVVRIT1JJWkVEX0tFWVNcIiBdXTsgdGhlblxuICAgICAgICBlY2hvIFwiJERTVEFDS19BVVRIT1JJWkVEX0tFWVNcIiA+IC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXNcbiAgICAgICAgdW5zZXQgJERTVEFDS19BVVRIT1JJWkVEX0tFWVNcbiAgICAgICAgZWNobyBcIlJvb3QgYXV0aG9yaXplZF9rZXlzIHNldFwiXG4gICAgZmlcblxuICAgIGlmIFtbIC1mIC9kc3RhY2svdXNlcl9jb25maWcgXV0gJiYganEgZW1wdHkgL2RzdGFjay91c2VyX2NvbmZpZyAyPi9kZXYvbnVsbDsgdGhlblxuICAgICAgICBpZiBbWyAkKGpxICdoYXMoXCJzc2hfYXV0aG9yaXplZF9rZXlzXCIpJyAvZHN0YWNrL3VzZXJfY29uZmlnIDI+L2Rldi9udWxsKSA9PSBcInRydWVcIiBdXTsgdGhlblxuICAgICAgICAgICAganEgLWogJy5zc2hfYXV0aG9yaXplZF9rZXlzJyAvZHN0YWNrL3VzZXJfY29uZmlnID4+IC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXNcbiAgICAgICAgICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMgaWYgdGhlcmUgYXJlIG11bHRpcGxlIGtleXNcbiAgICAgICAgICAgIGlmIFtbICQoY2F0IC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMgfCB3YyAtbCkgLWd0IDEgXV07IHRoZW5cbiAgICAgICAgICAgICAgICBzb3J0IC11IC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMgPiAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzLnRtcFxuICAgICAgICAgICAgICAgIG12IC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXMudG1wIC9ob21lL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXNcbiAgICAgICAgICAgIGZpXG4gICAgICAgICAgICBlY2hvIFwiU2V0IHJvb3QgYXV0aG9yaXplZF9rZXlzIGZyb20gdXNlciBwcmVmZXJlbmNlcywgdG90YWxcIiAkKGNhdCAvaG9tZS9yb290Ly5zc2gvYXV0aG9yaXplZF9rZXlzIHwgd2MgLWwpIFwia2V5c1wiXG4gICAgICAgIGZpXG4gICAgZmlcbmVsc2VcbiAgICBlY2hvIFwiV2FybmluZzogQ2Fubm90IGNyZWF0ZSAvaG9tZS9yb290Ly5zc2ggZGlyZWN0b3J5IChyZWFkLW9ubHkgZmlsZSBzeXN0ZW0/KVwiXG4gICAgZWNobyBcIlNraXBwaW5nIFNTSCBrZXkgc2V0dXBcIlxuZmlcblxuaWYgW1sgLVMgL3Zhci9ydW4vZHN0YWNrLnNvY2sgXV07IHRoZW5cbiAgICBleHBvcnQgRFNUQUNLX0FQUF9JRD0kKGN1cmwgLXMgLS11bml4LXNvY2tldCAvdmFyL3J1bi9kc3RhY2suc29jayBodHRwOi8vZHN0YWNrL0luZm8gfCBqcSAtaiAuYXBwX2lkKVxuZWxpZiBbWyAtUyAvdmFyL3J1bi90YXBwZC5zb2NrIF1dOyB0aGVuXG4gICAgZXhwb3J0IERTVEFDS19BUFBfSUQ9JChjdXJsIC1zIC0tdW5peC1zb2NrZXQgL3Zhci9ydW4vdGFwcGQuc29jayBodHRwOi8vZHN0YWNrL3BycGMvVGFwcGQuSW5mbyB8IGpxIC1qIC5hcHBfaWQpXG5maVxuIyBDaGVjayBpZiBEU1RBQ0tfR0FURVdBWV9ET01BSU4gaXMgbm90IHNldCwgdHJ5IHRvIGdldCBpdCBmcm9tIHVzZXJfY29uZmlnIG9yIGFwcC1jb21wb3NlLmpzb25cbiMgUHJpb3JpdHk6IHVzZXJfY29uZmlnID4gYXBwLWNvbXBvc2UuanNvblxuaWYgW1sgLXogXCIkRFNUQUNLX0dBVEVXQVlfRE9NQUlOXCIgXV07IHRoZW5cbiAgICAjIEZpcnN0IHRyeSB0byBnZXQgZnJvbSAvZHN0YWNrL3VzZXJfY29uZmlnIGlmIGl0IGV4aXN0cyBhbmQgaXMgdmFsaWQgSlNPTlxuICAgIGlmIFtbIC1mIC9kc3RhY2svdXNlcl9jb25maWcgXV0gJiYganEgZW1wdHkgL2RzdGFjay91c2VyX2NvbmZpZyAyPi9kZXYvbnVsbDsgdGhlblxuICAgICAgICBpZiBbWyAkKGpxICdoYXMoXCJkZWZhdWx0X2dhdGV3YXlfZG9tYWluXCIpJyAvZHN0YWNrL3VzZXJfY29uZmlnIDI+L2Rldi9udWxsKSA9PSBcInRydWVcIiBdXTsgdGhlblxuICAgICAgICAgICAgZXhwb3J0IERTVEFDS19HQVRFV0FZX0RPTUFJTj0kKGpxIC1qICcuZGVmYXVsdF9nYXRld2F5X2RvbWFpbicgL2RzdGFjay91c2VyX2NvbmZpZylcbiAgICAgICAgZmlcbiAgICBmaVxuXG4gICAgIyBJZiBzdGlsbCBub3Qgc2V0LCB0cnkgdG8gZ2V0IGZyb20gYXBwLWNvbXBvc2UuanNvblxuICAgIGlmIFtbIC16IFwiJERTVEFDS19HQVRFV0FZX0RPTUFJTlwiIF1dICYmIFtbICQoanEgJ2hhcyhcImRlZmF1bHRfZ2F0ZXdheV9kb21haW5cIiknIGFwcC1jb21wb3NlLmpzb24pID09IFwidHJ1ZVwiIF1dOyB0aGVuXG4gICAgICAgIGV4cG9ydCBEU1RBQ0tfR0FURVdBWV9ET01BSU49JChqcSAtaiAnLmRlZmF1bHRfZ2F0ZXdheV9kb21haW4nIGFwcC1jb21wb3NlLmpzb24pXG4gICAgZmlcbmZpXG5pZiBbWyAtbiBcIiREU1RBQ0tfR0FURVdBWV9ET01BSU5cIiBdXTsgdGhlblxuICAgIGV4cG9ydCBEU1RBQ0tfQVBQX0RPTUFJTj0kRFNUQUNLX0FQUF9JRFwiLlwiJERTVEFDS19HQVRFV0FZX0RPTUFJTlxuZmlcblxuZWNobyBcIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cIlxuZWNobyBcIlNjcmlwdCBleGVjdXRpb24gY29tcGxldGVkXCJcbmVjaG8gXCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXCJcbiIsInB1YmxpY19sb2dzIjp0cnVlLCJwdWJsaWNfc3lzaW5mbyI6dHJ1ZSwicHVibGljX3RjYmluZm8iOnRydWUsInJ1bm5lciI6ImRvY2tlci1jb21wb3NlIiwic2VjdXJlX3RpbWUiOmZhbHNlLCJzdG9yYWdlX2ZzIjoiemZzIiwidHByb3h5X2VuYWJsZWQiOnRydWV9"}}, "service_capabilities": {"supported_e2ee_versions": ["2"], "serving": "aggregator"}}}
\ No newline at end of file
From bdc3196d302eb5e1921c247a95b3e35e68ee5e16 Mon Sep 17 00:00:00 2001
From: winterstacks
Date: Wed, 16 Sep 2026 12:48:54 +0200
Subject: [PATCH 3/3] say what PRISM does on the network on the about page
Compute is quoted and paid in USDG or USDC, so a renter never has to hold
PRISM to rent a GPU. What staking buys is a lower rate on the machines
operators reserve for stakers: 177 base units per second against 222, about
twenty percent, on two of the six offers published today.
Signed-off-by: winterstacks
---
apps/web/app/about/page.tsx | 39 +++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/apps/web/app/about/page.tsx b/apps/web/app/about/page.tsx
index 31bcff8..a4ab212 100644
--- a/apps/web/app/about/page.tsx
+++ b/apps/web/app/about/page.tsx
@@ -26,9 +26,9 @@ export default function AboutPage() {
open SDK, an MCP server, and pay-per-job settlement over x402.
- The current service provides live, managed NVIDIA L40S capacity. A provider
- program for operator-owned infrastructure is in technical onboarding and is not yet
- available for production leases.
+ The current service provides live NVIDIA capacity across several card classes, sourced
+ from vetted providers. Operator-owned hardware is not part of the service today, so every
+ machine a customer rents is brokered, and the trust class on each offer says so.
@@ -45,7 +45,38 @@ export default function AboutPage() {
Workspaces are fresh containers, not confidential computing, so the infrastructure provider remains inside their trust boundary. Each vault item names the weakest workspace class it may be released into, and the default is above what the network can currently serve.
-
+
+
+ PRISM is the network's access token, not its currency. Compute is quoted and paid
+ in USDG or USDC, so a renter never has to hold PRISM to buy a GPU. What PRISM buys is a
+ cheaper rate on part of the fleet.
+
+ Staking unlocks discounted capacity
+
+ Operators can reserve a machine for stakers. Those offers are marked{" "}
+ staker_only and priced below open capacity: today they run at 177 base
+ units per second against 222 elsewhere, about 20 percent lower, and a wallet that has
+ not staked is refused rather than charged the higher rate. Stake matures before it
+ counts and unwinds through a cooldown, both enforced by the contract.
+
+ Where to check it
+
+ The token is{" "}
+
+ 0x0A1e0Cc7
+ {" "}
+ and the staking contract is{" "}
+
+ 0x7c4060e0
+
+ , both on Robinhood Chain. The staking contract is source-verified and has no owner, no
+ pause and no upgrade path, so the terms it was deployed with are the terms it keeps.
+ Live capacity and which offers are staker-only are published on the{" "}
+ offers endpoint.
+
+
+
+
Prism's protocol, smart contracts, service architecture, and application code are
developed in the open. Architecture and operational contracts are