@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
682 lines (648 loc) • 24.6 kB
JavaScript
import { createHash } from "node:crypto";
import {
existsSync,
lstatSync,
readFileSync,
readlinkSync,
realpathSync,
} from "node:fs";
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
export const RUNTIME_BOUNDARY_VERSION = "sellable-agent-runtime-boundary/v1";
export const RUNTIME_SANDBOX_INIT_PATH = "/usr/local/bin/sellable-agent-sandbox-init";
export const RUNTIME_EGRESS_HOSTS = Object.freeze([
"api.openai.com",
"app.sellable.dev",
"slack.com",
".slack.com",
]);
export const RUNTIME_SYSTEM_READ_ONLY_PATHS = Object.freeze([
"/usr",
"/etc/ssl",
"/etc/resolv.conf",
"/etc/hosts",
"/etc/nsswitch.conf",
"/etc/passwd",
"/etc/group",
]);
const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
const SLACK_ID = /^[CG][A-Z0-9]{8,31}$/;
const SLACK_MEMBER_ID = /^[UW][A-Z0-9]{8,31}$/;
const SHA256 = /^[a-f0-9]{64}$/;
const INPUT_KEYS = new Set([
"profileId",
"workspaceId",
"agentId",
"runtimeUid",
"runtimeGid",
"proxyUid",
"proxyGid",
"proxyPort",
"proxyExecutablePath",
"proxyExecutableDigest",
"proxyConfigPath",
"proxyConfigDigest",
"activeProfileRoot",
"runtimeStateRoot",
"mcpCredentialRoot",
"hermesSourceRoot",
"hermesExecutablePath",
"hermesExecutableDigest",
"mcpExecutablePath",
"bubblewrapPath",
"setprivPath",
"nftPath",
"cgroupRoot",
"serviceCredentialGeneration",
"policyHash",
"selectedChannelIds",
"defaultChannelId",
"memberAllowlist",
]);
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
function canonical(value) {
if (Array.isArray(value)) return value.map(canonical);
if (value && typeof value === "object") {
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
}
return value;
}
function stableJson(value) {
return JSON.stringify(canonical(value));
}
function safeAbsolute(value) {
return (
typeof value === "string" &&
isAbsolute(value) &&
resolve(value) === value &&
!/[\0\n\r,]/.test(value)
);
}
function inside(root, candidate) {
const rel = relative(root, candidate);
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
}
function exactKeys(value, expected) {
return (
value &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.keys(value).length === expected.size &&
Object.keys(value).every((key) => expected.has(key))
);
}
function runtimeProfilePath(contract) {
return `/runtime/profiles/${contract.profileId}`;
}
export function buildRuntimeIdentityLaunchArgs({
uid,
gid,
profileId,
hermesExecutablePath,
}) {
if (
!/^[1-9][0-9]{0,9}$/.test(uid ?? "") ||
!/^[1-9][0-9]{0,9}$/.test(gid ?? "") ||
!SAFE_ID.test(profileId ?? "") ||
!safeAbsolute(hermesExecutablePath)
) {
throw new Error("runtime identity launch rejected");
}
return [
"--reuid", uid,
"--regid", gid,
"--clear-groups",
"--no-new-privs",
"--bounding-set=-all",
"--inh-caps=-all",
"--ambient-caps=-all",
"/usr/bin/env",
"-C", `/runtime/profiles/${profileId}`,
hermesExecutablePath,
"--profile", profileId,
"gateway", "run",
];
}
function boundaryTableName(profileId, runtimeUid) {
return `sellable_agent_${sha256(`${profileId}\0${runtimeUid}`).slice(0, 16)}`;
}
export function buildRuntimeBoundaryContract(input) {
try {
if (!exactKeys(input, INPUT_KEYS)) throw new Error("boundary schema rejected");
const identityValues = [input.runtimeUid, input.proxyUid];
if (
!SAFE_ID.test(input.profileId) ||
!SAFE_ID.test(input.workspaceId) ||
!SAFE_ID.test(input.agentId) ||
!identityValues.every((value) => Number.isSafeInteger(value) && value > 0) ||
![input.runtimeGid, input.proxyGid].every((value) => Number.isSafeInteger(value) && value > 0) ||
input.runtimeUid === input.proxyUid ||
!Number.isSafeInteger(input.proxyPort) ||
input.proxyPort < 1024 ||
input.proxyPort > 65535 ||
!Number.isSafeInteger(input.serviceCredentialGeneration) ||
input.serviceCredentialGeneration < 1 ||
!SHA256.test(input.policyHash) ||
!SHA256.test(input.proxyExecutableDigest) ||
!SHA256.test(input.proxyConfigDigest) ||
!SHA256.test(input.hermesExecutableDigest)
) throw new Error("boundary identity rejected");
const pathKeys = [
"proxyExecutablePath",
"proxyConfigPath",
"activeProfileRoot",
"runtimeStateRoot",
"mcpCredentialRoot",
"hermesSourceRoot",
"hermesExecutablePath",
"mcpExecutablePath",
"bubblewrapPath",
"setprivPath",
"nftPath",
"cgroupRoot",
];
if (pathKeys.some((key) => !safeAbsolute(input[key]))) throw new Error("boundary path rejected");
if (
input.bubblewrapPath !== "/usr/bin/bwrap" ||
input.setprivPath !== "/usr/bin/setpriv" ||
!["/usr/sbin/nft", "/usr/bin/nft"].includes(input.nftPath) ||
input.cgroupRoot !== "/sys/fs/cgroup/sellable-agent" ||
input.mcpExecutablePath !== "/usr/local/lib/sellable-agent/external-runtime/mcp/bin/sellable-mcp" ||
!inside(input.hermesSourceRoot, input.hermesExecutablePath)
) throw new Error("boundary executable pin rejected");
if (
!Array.isArray(input.selectedChannelIds) ||
input.selectedChannelIds.length < 1 ||
input.selectedChannelIds.length > 100 ||
input.selectedChannelIds.some((value) => !SLACK_ID.test(value)) ||
new Set(input.selectedChannelIds).size !== input.selectedChannelIds.length ||
!input.selectedChannelIds.includes(input.defaultChannelId)
) throw new Error("boundary channel policy rejected");
if (
!Array.isArray(input.memberAllowlist) ||
input.memberAllowlist.length < 1 ||
input.memberAllowlist.length > 1000 ||
input.memberAllowlist.some((value) => !SLACK_MEMBER_ID.test(value)) ||
new Set(input.memberAllowlist).size !== input.memberAllowlist.length
) throw new Error("boundary member policy rejected");
const body = {
boundaryVersion: RUNTIME_BOUNDARY_VERSION,
profileId: input.profileId,
workspaceId: input.workspaceId,
agentId: input.agentId,
runtime: { uid: input.runtimeUid, gid: input.runtimeGid },
proxy: {
uid: input.proxyUid,
gid: input.proxyGid,
host: "127.0.0.1",
port: input.proxyPort,
executablePath: input.proxyExecutablePath,
executableDigest: input.proxyExecutableDigest,
configPath: input.proxyConfigPath,
configDigest: input.proxyConfigDigest,
allowedHosts: [...RUNTIME_EGRESS_HOSTS],
},
paths: Object.fromEntries(pathKeys.filter((key) => !["proxyExecutablePath", "proxyConfigPath"].includes(key)).map((key) => [key, input[key]])),
hermesExecutableDigest: input.hermesExecutableDigest,
serviceCredentialGeneration: input.serviceCredentialGeneration,
policyHash: input.policyHash,
selectedChannelIds: [...input.selectedChannelIds],
defaultChannelId: input.defaultChannelId,
memberAllowlist: [...input.memberAllowlist],
systemReadOnlyPaths: [...RUNTIME_SYSTEM_READ_ONLY_PATHS],
limits: {
pidsMax: 128,
memoryMax: 1024 * 1024 * 1024,
cpuMax: "100000 100000",
},
firewall: {
family: "inet",
table: boundaryTableName(input.profileId, input.runtimeUid),
runtimeUid: input.runtimeUid,
proxyHost: "127.0.0.1",
proxyPort: input.proxyPort,
defaultRuntimeAction: "reject",
},
};
return { ok: true, contract: Object.freeze({ ...body, contractDigest: sha256(stableJson(body)) }) };
} catch {
return { ok: false, code: "runtime_boundary_contract_rejected" };
}
}
function addParentDirs(args, targets) {
const seen = new Set();
for (const target of targets) {
let cursor = dirname(target);
const parents = [];
while (cursor !== "/" && cursor !== ".") {
parents.push(cursor);
cursor = dirname(cursor);
}
for (const parent of parents.reverse()) {
if (!seen.has(parent)) {
args.push("--dir", parent);
seen.add(parent);
}
}
}
}
function runtimeEnvironment(contract) {
const profileRoot = runtimeProfilePath(contract);
return {
PATH: `${dirname(contract.paths.hermesExecutablePath)}:/usr/local/bin:/usr/bin:/bin`,
HOME: `${profileRoot}/home`,
HERMES_HOME: "/runtime",
NODE_ENV: "production",
HTTPS_PROXY: `http://${contract.proxy.host}:${contract.proxy.port}`,
HTTP_PROXY: `http://${contract.proxy.host}:${contract.proxy.port}`,
NO_PROXY: "127.0.0.1,localhost",
SLACK_HOME_CHANNEL: contract.defaultChannelId,
SLACK_ALLOWED_CHANNELS: contract.selectedChannelIds.join(","),
SLACK_ALLOWED_USERS: contract.memberAllowlist.join(","),
SLACK_REQUIRE_MENTION: "true",
SELLABLE_AGENT_RUNTIME: "1",
SELLABLE_AGENT_RUNTIME_UID: String(contract.runtime.uid),
SELLABLE_AGENT_RUNTIME_GID: String(contract.runtime.gid),
SELLABLE_AGENT_HERMES_EXECUTABLE: contract.paths.hermesExecutablePath,
SELLABLE_AGENT_SETPRIV_EXECUTABLE: contract.paths.setprivPath,
SELLABLE_SLACK_APP_TOKEN_FD: "8",
SELLABLE_SLACK_BOT_TOKEN_FD: "9",
SELLABLE_AGENT_PROFILE_ID: contract.profileId,
SELLABLE_AGENT_WORKSPACE_ID: contract.workspaceId,
SELLABLE_AGENT_ID: contract.agentId,
SELLABLE_AGENT_CREDENTIAL_GENERATION: String(contract.serviceCredentialGeneration),
SELLABLE_AGENT_POLICY_HASH: contract.policyHash,
SELLABLE_AGENT_SELECTED_CHANNEL_IDS: JSON.stringify(contract.selectedChannelIds),
SELLABLE_LOCK_WORKSPACE_ID: contract.workspaceId,
SELLABLE_REQUIRE_WORKSPACE_LOCK: "1",
SELLABLE_CONFIG_PATH: `${profileRoot}/sellable/mcp.json`,
SELLABLE_AGENT_RUNTIME_SECRET_ROOT: "/run/sellable-agent",
SELLABLE_AGENT_SERVICE_CREDENTIAL_FILE: "/run/sellable-agent/service-credential",
};
}
export function buildRuntimeBoundaryLaunchSpec(contract) {
try {
if (!contract || contract.boundaryVersion !== RUNTIME_BOUNDARY_VERSION || !SHA256.test(contract.contractDigest)) {
throw new Error("boundary contract missing");
}
const active = contract.paths.activeProfileRoot;
const state = contract.paths.runtimeStateRoot;
const credential = join(contract.paths.mcpCredentialRoot, contract.profileId);
const profileTarget = runtimeProfilePath(contract);
const readOnlyProfileBinds = [
[join(active, "agent-profile.json"), `${profileTarget}/agent-profile.json`],
[join(active, "config.yaml"), `${profileTarget}/config.yaml`],
[join(active, "SOUL.md"), `${profileTarget}/SOUL.md`],
[join(active, "skills"), `${profileTarget}/skills`],
[join(active, "sellable"), `${profileTarget}/sellable`],
];
const args = [
"--die-with-parent",
"--new-session",
"--unshare-pid",
"--unshare-ipc",
"--unshare-uts",
"--unshare-cgroup",
"--clearenv",
"--unsetenv",
"PWD",
"--cap-drop",
"ALL",
"--cap-add",
"CAP_SETUID",
"--cap-add",
"CAP_SETGID",
"--cap-add",
"CAP_SETPCAP",
];
const bindTargets = [
...contract.systemReadOnlyPaths,
contract.paths.hermesSourceRoot,
"/runtime",
"/run/sellable-agent",
"/run/bootstrap",
...readOnlyProfileBinds.map(([, target]) => target),
"/proc",
"/dev",
"/tmp",
];
addParentDirs(args, bindTargets);
for (const path of contract.systemReadOnlyPaths) args.push("--ro-bind", path, path);
args.push(
"--symlink", "usr/bin", "/bin",
"--symlink", "usr/lib", "/lib",
"--symlink", "usr/lib64", "/lib64",
"--symlink", "usr/sbin", "/sbin",
);
args.push("--ro-bind", contract.paths.hermesSourceRoot, contract.paths.hermesSourceRoot);
args.push("--bind", state, "/runtime");
for (const [source, target] of readOnlyProfileBinds) args.push("--ro-bind", source, target);
args.push("--ro-bind", credential, "/run/sellable-agent");
args.push("--tmpfs", "/run/bootstrap");
args.push(
"--perms", "0600", "--file", "8", "/run/bootstrap/slack-app",
"--perms", "0600", "--file", "9", "/run/bootstrap/slack-bot",
);
args.push(
"--proc", "/proc",
"--dev", "/dev",
"--tmpfs", "/tmp",
"--remount-ro", "/",
);
const env = runtimeEnvironment(contract);
for (const [name, value] of Object.entries(env)) args.push("--setenv", name, value);
args.push(
"--chdir",
"/",
RUNTIME_SANDBOX_INIT_PATH,
);
return {
ok: true,
command: contract.paths.bubblewrapPath,
args,
env: { PATH: "/usr/sbin:/usr/bin:/sbin:/bin" },
inheritedFileDescriptors: [8, 9],
runtimeEnvironment: env,
profileTarget,
cgroupPath: join(contract.paths.cgroupRoot, contract.profileId),
shell: false,
};
} catch {
return { ok: false, code: "runtime_boundary_launch_rejected" };
}
}
export function buildRuntimeBoundaryNftProgram(contract) {
if (!contract?.firewall || contract.firewall.defaultRuntimeAction !== "reject") {
return { ok: false, code: "runtime_boundary_firewall_rejected" };
}
const { table, runtimeUid, proxyHost, proxyPort } = contract.firewall;
if (!/^sellable_agent_[a-f0-9]{16}$/.test(table) || proxyHost !== "127.0.0.1") {
return { ok: false, code: "runtime_boundary_firewall_rejected" };
}
const program = [
`table inet ${table} {`,
" chain output {",
" type filter hook output priority -20; policy accept;",
` meta skuid ${runtimeUid} ip daddr 127.0.0.1 tcp dport ${proxyPort} accept`,
` meta skuid ${runtimeUid} ip6 daddr ::1 tcp dport ${proxyPort} accept`,
` meta skuid ${runtimeUid} reject with icmpx type admin-prohibited`,
" }",
"}",
"",
].join("\n");
return {
ok: true,
table,
deleteArgs: ["delete", "table", "inet", table],
applyArgs: ["-f", "-"],
program,
programDigest: sha256(program),
};
}
export function expectedRuntimeCgroup(contract) {
const path = join(contract.paths.cgroupRoot, contract.profileId);
return {
path,
files: {
"pids.max": String(contract.limits.pidsMax),
"memory.max": String(contract.limits.memoryMax),
"cpu.max": contract.limits.cpuMax,
},
};
}
function parseStatus(source) {
return Object.fromEntries(
source.split(/\r?\n/).filter(Boolean).map((line) => {
const index = line.indexOf(":");
return [line.slice(0, index), line.slice(index + 1).trim()];
}),
);
}
function parseMountInfo(source) {
const entries = [];
for (const line of source.split(/\r?\n/).filter(Boolean)) {
const separator = line.indexOf(" - ");
if (separator < 0) continue;
const left = line.slice(0, separator).split(" ");
entries.push({ mountPoint: left[4], options: new Set(left[5].split(",")) });
}
return entries;
}
function requiredMount(entries, mountPoint, option) {
return entries.some((entry) => entry.mountPoint === mountPoint && entry.options.has(option));
}
function expectedHermesCommand(contract) {
const executable = safeRead(contract.paths.hermesExecutablePath, null);
if (sha256(executable) !== contract.hermesExecutableDigest) throw new Error("Hermes executable drift rejected");
const firstLine = executable.subarray(0, 4096).toString("utf8").split(/\r?\n/, 1)[0];
if (!firstLine.startsWith("#!")) return [contract.paths.hermesExecutablePath];
const interpreter = firstLine.slice(2).trim();
if (!safeAbsolute(interpreter) || /\s/.test(interpreter)) throw new Error("Hermes shebang rejected");
return [interpreter, contract.paths.hermesExecutablePath];
}
export function runtimeBoundaryNftRulesMatch(contract, source) {
if (typeof source !== "string" || source.length > 128 * 1024) return false;
const expected = buildRuntimeBoundaryNftProgram(contract);
if (!expected.ok) return false;
const tokens = (value) => {
const parsed = value
.replace(/#.*$/gm, "")
.match(/[{};]|[^\s{};]+/g) ?? [];
const normalized = [];
for (let index = 0; index < parsed.length; index += 1) {
if (
parsed[index] === "icmpx" &&
parsed[index + 1] === "type" &&
parsed[index + 2] === "admin-prohibited"
) {
normalized.push("icmpx", "admin-prohibited");
index += 2;
continue;
}
normalized.push(parsed[index]);
}
return normalized;
};
return stableJson(tokens(source)) === stableJson(tokens(expected.program));
}
function safeRead(path, encoding = "utf8") {
const link = lstatSync(path);
if (link.isSymbolicLink() || !link.isFile()) throw new Error("boundary observation path rejected");
return readFileSync(path, encoding);
}
export function observeRuntimeBoundary({
contract,
pid,
procRoot = "/proc",
cgroupRoot = contract?.paths?.cgroupRoot,
nftRuleset,
proxyObservation,
}) {
try {
if (!Number.isSafeInteger(pid) || pid < 2 || !safeAbsolute(procRoot) || !safeAbsolute(cgroupRoot)) {
throw new Error("boundary observation identity rejected");
}
const processRoot = join(procRoot, String(pid));
const status = parseStatus(safeRead(join(processRoot, "status")));
const uid = Number(status.Uid?.split(/\s+/)[0]);
const gid = Number(status.Gid?.split(/\s+/)[0]);
if (
uid !== contract.runtime.uid ||
gid !== contract.runtime.gid ||
status.NoNewPrivs !== "1" ||
!/^0+$/.test(status.CapEff ?? "")
) throw new Error("runtime privilege observation rejected");
const namespaceChecks = ["mnt", "pid", "ipc", "uts"].map((name) => {
const runtimeNs = readlinkSync(join(processRoot, "ns", name));
const hostNs = readlinkSync(join(procRoot, "1", "ns", name));
return runtimeNs !== hostNs;
});
if (namespaceChecks.some((value) => !value)) throw new Error("runtime namespace observation rejected");
const mounts = parseMountInfo(safeRead(join(processRoot, "mountinfo")));
const profileTarget = runtimeProfilePath(contract);
const requiredReadOnlyMounts = [
"/",
contract.paths.hermesSourceRoot,
`${profileTarget}/agent-profile.json`,
`${profileTarget}/config.yaml`,
`${profileTarget}/SOUL.md`,
`${profileTarget}/skills`,
`${profileTarget}/sellable`,
"/run/sellable-agent",
];
if (
requiredReadOnlyMounts.some((path) => !requiredMount(mounts, path, "ro")) ||
!requiredMount(mounts, "/runtime", "rw")
) throw new Error("runtime mount observation rejected");
const cgroupText = safeRead(join(processRoot, "cgroup")).trim();
const expectedGroup = `/sellable-agent/${contract.profileId}`;
if (!cgroupText.split(/\r?\n/).some((line) => line === `0::${expectedGroup}`)) {
throw new Error("runtime cgroup membership rejected");
}
const cgroup = expectedRuntimeCgroup(contract);
const observedCgroupPath = join(cgroupRoot, contract.profileId);
for (const [name, value] of Object.entries(cgroup.files)) {
if (safeRead(join(observedCgroupPath, name)).trim() !== value) throw new Error("runtime cgroup limit rejected");
}
const environment = Object.fromEntries(
safeRead(join(processRoot, "environ"), null)
.toString("utf8")
.split("\0")
.filter(Boolean)
.map((entry) => [entry.slice(0, entry.indexOf("=")), entry.slice(entry.indexOf("=") + 1)]),
);
const expectedEnv = runtimeEnvironment(contract);
if (
stableJson(environment) !== stableJson(expectedEnv) ||
Object.keys(environment).some((name) => /SLACK_(?:APP|BOT)_TOKEN$|HERMES_SESSION_/.test(name))
) throw new Error("runtime environment observation rejected");
const cmdline = safeRead(join(processRoot, "cmdline"), null).toString("utf8").split("\0").filter(Boolean);
const expectedCommand = [
...expectedHermesCommand(contract),
"--profile",
contract.profileId,
"gateway",
"run",
];
if (stableJson(cmdline) !== stableJson(expectedCommand)) throw new Error("runtime executable observation rejected");
for (const [fd, name] of [[8, "slack-app"], [9, "slack-bot"]]) {
const descriptor = lstatSync(join(processRoot, "fd", String(fd)));
if (!descriptor.isSymbolicLink()) throw new Error("runtime inherited descriptor rejected");
const target = readlinkSync(join(processRoot, "fd", String(fd)));
if (target !== `/run/bootstrap/${name} (deleted)`) {
throw new Error("runtime inherited descriptor source rejected");
}
}
if (!runtimeBoundaryNftRulesMatch(contract, nftRuleset)) throw new Error("runtime firewall observation rejected");
if (
!proxyObservation?.ok ||
proxyObservation.uid !== contract.proxy.uid ||
proxyObservation.gid !== contract.proxy.gid ||
proxyObservation.host !== contract.proxy.host ||
proxyObservation.port !== contract.proxy.port ||
proxyObservation.executableDigest !== contract.proxy.executableDigest ||
proxyObservation.configDigest !== contract.proxy.configDigest ||
stableJson(proxyObservation.allowedHosts) !== stableJson(contract.proxy.allowedHosts)
) throw new Error("runtime egress proxy observation rejected");
return {
ok: true,
boundaryVersion: contract.boundaryVersion,
contractDigest: contract.contractDigest,
pid,
runtimeUid: uid,
cgroup: expectedGroup,
firewallTable: contract.firewall.table,
proxy: {
uid: contract.proxy.uid,
host: contract.proxy.host,
port: contract.proxy.port,
executableDigest: contract.proxy.executableDigest,
configDigest: contract.proxy.configDigest,
allowedHosts: [...contract.proxy.allowedHosts],
},
checks: {
namespacesIsolated: true,
rootReadOnly: true,
revisionReadOnly: true,
runtimeStateWritable: true,
noNewPrivileges: true,
capabilitiesDropped: true,
resourceLimits: true,
directEgressDenied: true,
allowlistedProxyOnly: true,
inheritedSlackDescriptors: true,
},
};
} catch {
return { ok: false, code: "runtime_boundary_observation_rejected" };
}
}
export function inspectRuntimeBoundaryInputs(contract) {
try {
const regularFiles = [
contract.paths.bubblewrapPath,
contract.paths.setprivPath,
contract.paths.nftPath,
contract.paths.hermesExecutablePath,
contract.paths.mcpExecutablePath,
RUNTIME_SANDBOX_INIT_PATH,
contract.proxy.executablePath,
contract.proxy.configPath,
join(contract.paths.activeProfileRoot, "agent-profile.json"),
join(contract.paths.activeProfileRoot, "config.yaml"),
join(contract.paths.activeProfileRoot, "SOUL.md"),
join(contract.paths.activeProfileRoot, "sellable", "mcp.json"),
join(contract.paths.mcpCredentialRoot, contract.profileId, "service-credential"),
];
const directories = [
contract.paths.activeProfileRoot,
contract.paths.runtimeStateRoot,
contract.paths.mcpCredentialRoot,
contract.paths.hermesSourceRoot,
join(contract.paths.activeProfileRoot, "skills"),
join(contract.paths.activeProfileRoot, "sellable"),
];
for (const path of regularFiles) {
const link = lstatSync(path);
if (link.isSymbolicLink() || !link.isFile() || (link.mode & 0o022) !== 0) throw new Error("unsafe boundary file");
}
for (const path of directories) {
const link = lstatSync(path);
if (link.isSymbolicLink() || !link.isDirectory() || (link.mode & 0o022) !== 0) throw new Error("unsafe boundary directory");
}
if (
sha256(readFileSync(contract.proxy.executablePath)) !== contract.proxy.executableDigest ||
sha256(readFileSync(contract.proxy.configPath)) !== contract.proxy.configDigest ||
sha256(readFileSync(contract.paths.hermesExecutablePath)) !== contract.hermesExecutableDigest ||
realpathSync(contract.paths.hermesSourceRoot) !== contract.paths.hermesSourceRoot ||
!inside(contract.paths.hermesSourceRoot, realpathSync(contract.paths.hermesExecutablePath))
) throw new Error("boundary byte identity rejected");
return {
ok: true,
contractDigest: contract.contractDigest,
proxyExecutableDigest: contract.proxy.executableDigest,
proxyConfigDigest: contract.proxy.configDigest,
};
} catch {
return { ok: false, code: "runtime_boundary_inputs_rejected" };
}
}