@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
2,138 lines • 70.3 kB
JavaScript
import { createHash, randomUUID } from "node:crypto";
import {
chmodSync,
chownSync,
closeSync,
constants,
existsSync,
fstatSync,
fsyncSync,
lstatSync,
mkdirSync,
openSync,
readFileSync,
readdirSync,
realpathSync,
renameSync,
rmSync,
writeFileSync,
} from "node:fs";
import { createConnection } from "node:net";
import {
basename,
dirname,
isAbsolute,
join,
relative,
resolve,
} from "node:path";
import {
HERMES_AGENT_BRIDGE_CONTRACT,
verifyHermesAgentBridge,
} from "./hermes-bridge.mjs";
import { runBoundedChildProcess } from "./provisioning-adapter.mjs";
import {
buildRuntimeBoundaryContract,
observeRuntimeBoundary,
} from "./runtime-boundary.mjs";
import {
observeRuntimeEgressProxy,
validateRuntimeEgressProxyConfig,
} from "./runtime-egress-proxy.mjs";
const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
const SHA256_16 = /^[a-f0-9]{16}$/;
const SHA256 = /^[a-f0-9]{64}$/;
export const EXTERNAL_RUNTIME_CLOSURE_ROOT =
"/usr/local/lib/sellable-agent/external-runtime";
export const EXTERNAL_RUNTIME_MANIFEST_PATH = `${EXTERNAL_RUNTIME_CLOSURE_ROOT}/manifest.json`;
export const EXTERNAL_MCP_ENTRYPOINT = `${EXTERNAL_RUNTIME_CLOSURE_ROOT}/mcp/bin/sellable-mcp`;
export const EXTERNAL_HERMES_ENTRYPOINT = `${EXTERNAL_RUNTIME_CLOSURE_ROOT}/hermes/venv/bin/hermes`;
const EXTERNAL_RUNTIME_MANIFEST_VERSION =
"sellable-agent-external-runtime-closure/v1";
const ACTIONS = new Set([
"INSTALL_MCP_CREDENTIAL",
"INSTALL_PROVIDER_CREDENTIAL",
"INSTALL_RUNTIME_SERVICE_ENV",
"OBSERVE_RUNTIME_LIFECYCLE",
"CLEAN_PROFILE_SECRETS",
"CHOWN_PROFILE",
"REMOVE_PROFILE",
"OBSERVE_RUNTIME",
"OBSERVE_RUNTIME_DRIFT",
"PROBE_CONTAINMENT",
"INSTALL_HERMES_BRIDGE",
"VERIFY_HERMES_BRIDGE",
"CONTROL_RUNTIME",
]);
const PROFILE_ENTRIES = Object.freeze([
[".", "directory"],
["agent-profile.json", "file"],
["config.yaml", "file"],
["sellable", "directory"],
["sellable/mcp.json", "file"],
["SOUL.md", "file"],
["skills", "directory"],
["skills/sellable", "directory"],
["skills/sellable/SKILL.md", "file"],
]);
const ACTION_KEYS = Object.freeze({
INSTALL_MCP_CREDENTIAL: [
"action",
"profileId",
"operationId",
"fence",
"sourceRequestFile",
"generation",
"fingerprint",
],
INSTALL_PROVIDER_CREDENTIAL: [
"action",
"profileId",
"operationId",
"fence",
"fingerprint",
],
INSTALL_RUNTIME_SERVICE_ENV: [
"action",
"profileId",
"operationId",
"fence",
"revisionId",
"profileDigest",
],
OBSERVE_RUNTIME_LIFECYCLE: [
"action",
"profileId",
"operationId",
"fence",
"lifecycle",
],
CLEAN_PROFILE_SECRETS: ["action", "profileId", "operationId", "fence"],
CHOWN_PROFILE: [
"action",
"profileId",
"operationId",
"fence",
"profileRoot",
"revisionId",
"profileDigest",
],
REMOVE_PROFILE: [
"action",
"profileId",
"operationId",
"fence",
"slot",
"revisionId",
],
OBSERVE_RUNTIME: ["action", "profileId", "operationId", "fence"],
OBSERVE_RUNTIME_DRIFT: ["action", "profileId", "operationId", "fence"],
PROBE_CONTAINMENT: [
"action",
"profileId",
"operationId",
"fence",
"profileRoot",
"revisionId",
],
INSTALL_HERMES_BRIDGE: ["action", "profileId", "operationId", "fence"],
VERIFY_HERMES_BRIDGE: ["action", "profileId", "operationId", "fence"],
CONTROL_RUNTIME: ["action", "profileId", "operationId", "fence", "control"],
});
function privateRoot(value) {
if (!isAbsolute(value) || !existsSync(value))
throw new Error("helper root missing");
const link = lstatSync(value);
if (link.isSymbolicLink() || !link.isDirectory() || (link.mode & 0o077) !== 0)
throw new Error("helper root rejected");
return realpathSync(value);
}
function profileStorageRoot(value) {
if (!isAbsolute(value) || !existsSync(value))
throw new Error("profile storage root missing");
const link = lstatSync(value);
const mode = link.mode & 0o777;
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(mode & 0o700) !== 0o700 ||
(mode & 0o066) !== 0
)
throw new Error("profile storage root rejected");
return realpathSync(value);
}
function immutableSourceRoot(value) {
if (!isAbsolute(value) || !existsSync(value))
throw new Error("source root missing");
const link = lstatSync(value);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(link.mode & 0o022) !== 0
) {
throw new Error("source root rejected");
}
return realpathSync(value);
}
function fsyncDirectory(value) {
const descriptor = openSync(value, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
}
function atomicCredential(pathValue, secret, uid, gid) {
const temp = join(
dirname(pathValue),
`.${basename(pathValue)}.${randomUUID()}.tmp`
);
writeFileSync(temp, `${secret}\n`, { mode: 0o600, flag: "wx" });
chmodSync(temp, 0o600);
const tempOwner = lstatSync(temp);
if (tempOwner.uid !== uid || tempOwner.gid !== gid) chownSync(temp, uid, gid);
const descriptor = openSync(temp, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
renameSync(temp, pathValue);
fsyncDirectory(dirname(pathValue));
}
function providerCredentialPath(profile, profileId) {
const runtimeStateRoot = safeRuntimeStateRoot(profile);
const profilesRoot = join(runtimeStateRoot, "profiles");
const profileRoot = join(profilesRoot, profileId);
const runtimeHome = join(profileRoot, "home");
for (const pathValue of [profilesRoot, profileRoot, runtimeHome]) {
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(link.mode & 0o777) !== 0o700 ||
link.uid !== profile.runtimeUid ||
link.gid !== profile.runtimeGid
)
throw new Error("provider runtime home rejected");
}
const codexRoot = join(runtimeHome, ".codex");
if (!existsSync(codexRoot)) {
mkdirSync(codexRoot, { mode: 0o700 });
chownSync(codexRoot, profile.runtimeUid, profile.runtimeGid);
}
const codex = lstatSync(codexRoot);
if (
codex.isSymbolicLink() ||
!codex.isDirectory() ||
(codex.mode & 0o777) !== 0o700 ||
codex.uid !== profile.runtimeUid ||
codex.gid !== profile.runtimeGid
)
throw new Error("provider credential root rejected");
return join(codexRoot, "auth.json");
}
function installProviderCredential(profile, request, config) {
if (
!SHA256.test(request.fingerprint ?? "") ||
request.fingerprint !== profile.providerCredentialFingerprint ||
(profile.providerCredentialSourcePath !==
"/var/lib/sellable-agent-bootstrap/provider-auth" &&
config.disposableFixture !== true)
)
throw new Error("provider credential identity rejected");
const sourceRoot = privateRoot(dirname(profile.providerCredentialSourcePath));
const source = readPrivateDirectFile(
sourceRoot,
profile.providerCredentialSourcePath
).trim();
if (
!source ||
source.length > 1024 * 1024 ||
sha256(source) !== request.fingerprint
) {
throw new Error("provider credential source rejected");
}
const parsed = JSON.parse(source);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("provider credential JSON rejected");
}
const target = providerCredentialPath(profile, request.profileId);
atomicCredential(target, source, profile.runtimeUid, profile.runtimeGid);
return {
ok: true,
profileId: request.profileId,
fingerprint: request.fingerprint,
path: target,
};
}
function removeProviderCredential(profile, profileId) {
const target = providerCredentialPath(profile, profileId);
if (existsSync(target)) {
const link = lstatSync(target);
if (link.isSymbolicLink() || !link.isFile())
throw new Error("provider credential cleanup rejected");
rmSync(target, { force: true });
fsyncDirectory(dirname(target));
}
}
function observeProviderCredential(profile, profileId) {
const target = providerCredentialPath(profile, profileId);
const link = lstatSync(target);
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o777) !== 0o600 ||
link.uid !== profile.runtimeUid ||
link.gid !== profile.runtimeGid
)
throw new Error("provider credential readback rejected");
return sha256(readFileSync(target, "utf8").trim());
}
function applyRuntimeOwnership(pathValue, uid, gid) {
const link = lstatSync(pathValue);
if (link.isSymbolicLink()) throw new Error("profile symlink rejected");
if (link.isDirectory()) {
for (const name of readdirSync(pathValue))
applyRuntimeOwnership(join(pathValue, name), uid, gid);
chownSync(pathValue, uid, gid);
chmodSync(pathValue, 0o500);
} else if (link.isFile()) {
chownSync(pathValue, uid, gid);
chmodSync(pathValue, 0o400);
} else {
throw new Error("profile entry rejected");
}
}
function profileTarget(profile, request, allowedSlots) {
const root = profileStorageRoot(profile.profilesRoot);
const slot = request.slot;
if (!allowedSlots.includes(slot)) throw new Error("profile slot rejected");
let name;
if (slot === "ACTIVE") name = request.profileId;
else if (slot === "BACKUP") name = `${request.profileId}.rollback-prior-good`;
else {
if (
!SAFE_ID.test(request.revisionId ?? "") ||
!/^[1-9][0-9]{0,19}$/.test(request.fence ?? "")
) {
throw new Error("profile revision binding rejected");
}
name = `${request.profileId}.staging-${request.revisionId}-${request.fence}`;
}
const target = join(root, name);
if (dirname(target) !== root) throw new Error("profile target rejected");
return { root, target };
}
export function resolveContainmentProfileTarget(profile, request) {
const active = profileTarget(profile, { ...request, slot: "ACTIVE" }, [
"ACTIVE",
]);
const staging = profileTarget(profile, { ...request, slot: "STAGING" }, [
"STAGING",
]);
const provided = resolve(request.profileRoot ?? "");
if (!existsSync(provided) || lstatSync(provided).isSymbolicLink()) {
throw new Error("containment profile rejected");
}
const requested = realpathSync(provided);
if (requested === active.target) return { ...active, slot: "ACTIVE" };
if (requested === staging.target) return { ...staging, slot: "STAGING" };
throw new Error("containment profile rejected");
}
function observeProfileEntries(profileRoot) {
const entries = [];
const walk = (pathValue, logicalPath) => {
const link = lstatSync(pathValue);
if (link.isSymbolicLink()) throw new Error("profile symlink rejected");
const type = link.isDirectory()
? "directory"
: link.isFile()
? "file"
: "other";
entries.push({
path: logicalPath,
type,
uid: link.uid,
gid: link.gid,
mode: link.mode & 0o777,
});
if (type === "directory") {
for (const name of readdirSync(pathValue).sort()) {
walk(
join(pathValue, name),
logicalPath === "." ? name : `${logicalPath}/${name}`
);
}
}
};
walk(profileRoot, ".");
return entries;
}
function confinedRequest(requestPath, config) {
const root = privateRoot(config.helperRequestRoot);
return JSON.parse(readPrivateDirectFile(root, requestPath));
}
function readPrivateDirectFile(root, pathValue) {
const value = resolve(pathValue ?? "");
if (realpathSync(dirname(value)) !== root)
throw new Error("private file confinement failed");
const before = lstatSync(value);
if (
before.isSymbolicLink() ||
!before.isFile() ||
(before.mode & 0o777) !== 0o600
) {
throw new Error("private file rejected");
}
const descriptor = openSync(
value,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)
);
try {
const after = fstatSync(descriptor);
if (
!after.isFile() ||
after.dev !== before.dev ||
after.ino !== before.ino ||
(after.mode & 0o777) !== 0o600
) {
throw new Error("private file changed during open");
}
return readFileSync(descriptor, "utf8");
} finally {
closeSync(descriptor);
}
}
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function stableJson(value) {
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
if (value && typeof value === "object") {
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
.join(",")}}`;
}
return JSON.stringify(value);
}
function exactKeys(value, keys) {
return Boolean(
value &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.keys(value).sort().join("\0") === [...keys].sort().join("\0")
);
}
function safeRuntimeStateRoot(profile) {
const root = privateRoot(profile.runtimeStateRoot);
const owner = lstatSync(root);
if (owner.uid !== profile.runtimeUid || owner.gid !== profile.runtimeGid) {
throw new Error("runtime state ownership rejected");
}
return root;
}
function ensureOwnedRuntimeDirectory(pathValue, root, uid, gid) {
if (dirname(pathValue) !== root || existsSync(pathValue)) {
if (!existsSync(pathValue))
throw new Error("runtime directory confinement failed");
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(link.mode & 0o777) !== 0o700 ||
link.uid !== uid ||
link.gid !== gid
)
throw new Error("runtime directory rejected");
return;
}
mkdirSync(pathValue, { mode: 0o700 });
const owner = lstatSync(pathValue);
if (owner.uid !== uid || owner.gid !== gid) chownSync(pathValue, uid, gid);
chmodSync(pathValue, 0o700);
fsyncDirectory(root);
}
function validateActiveManifest(
target,
request,
profile,
allowFileDrift = false
) {
const entries = observeProfileEntries(target);
if (
JSON.stringify(entries.map(({ path, type }) => [path, type]).sort()) !==
JSON.stringify([...PROFILE_ENTRIES].sort()) ||
entries.some(
(entry) =>
entry.uid !== profile.runtimeUid ||
entry.gid !== profile.runtimeGid ||
entry.mode !== (entry.type === "directory" ? 0o500 : 0o400)
)
) {
throw new Error("active profile inventory rejected");
}
const manifest = JSON.parse(
readFileSync(join(target, "agent-profile.json"), "utf8")
);
if (
manifest.profileId !== request.profileId ||
manifest.revisionId !== request.revisionId ||
manifest.profileDigest !== request.profileDigest ||
!SHA256.test(manifest.profileDigest ?? "") ||
!SAFE_ID.test(manifest.workspaceId ?? "") ||
!SAFE_ID.test(manifest.agentId ?? "") ||
!Number.isSafeInteger(manifest.serviceCredentialGeneration) ||
manifest.serviceCredentialGeneration < 1 ||
!SHA256.test(manifest.policyHash ?? "") ||
!Array.isArray(manifest.slackChannelIds) ||
manifest.slackChannelIds.length === 0 ||
manifest.slackChannelIds.some((id) => !/^C[A-Z0-9]{8,20}$/.test(id)) ||
JSON.stringify(manifest.slackChannelIds) !==
JSON.stringify([...new Set(manifest.slackChannelIds)].sort()) ||
!manifest.slackChannelIds.includes(manifest.slackDefaultChannelId) ||
!Array.isArray(manifest.slackMemberAllowlist) ||
manifest.slackMemberAllowlist.length === 0 ||
manifest.slackMemberAllowlist.some(
(id) => !/^[UW][A-Z0-9]{8,20}$/.test(id)
) ||
JSON.stringify(manifest.slackMemberAllowlist) !==
JSON.stringify([...new Set(manifest.slackMemberAllowlist)].sort()) ||
!exactKeys(manifest.fileHashes, [
"config.yaml",
"sellable/mcp.json",
"SOUL.md",
"skills/sellable/SKILL.md",
])
)
throw new Error("active profile manifest rejected");
for (const [logicalPath, digest] of Object.entries(manifest.fileHashes)) {
if (
!SHA256.test(digest) ||
(!allowFileDrift &&
sha256(readFileSync(join(target, logicalPath))) !== digest)
) {
throw new Error("active profile file hash rejected");
}
}
const { profileDigest: _profileDigest, ...digestBody } = manifest;
if (sha256(stableJson(digestBody)) !== manifest.profileDigest)
throw new Error("active profile digest rejected");
return manifest;
}
export function observeRuntimeProfileFiles(profileRoot) {
const target = resolve(profileRoot ?? "");
if (!isAbsolute(target) || !existsSync(target))
throw new Error("runtime profile drift root missing");
const root = lstatSync(target);
if (
root.isSymbolicLink() ||
!root.isDirectory() ||
(root.mode & 0o022) !== 0
) {
throw new Error("runtime profile drift root rejected");
}
const manifestPath = join(target, "agent-profile.json");
const manifestLink = lstatSync(manifestPath);
if (
manifestLink.isSymbolicLink() ||
!manifestLink.isFile() ||
(manifestLink.mode & 0o022) !== 0
) {
throw new Error("runtime profile drift manifest rejected");
}
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
if (
!exactKeys(manifest.fileHashes, [
"config.yaml",
"sellable/mcp.json",
"SOUL.md",
"skills/sellable/SKILL.md",
])
) {
throw new Error("runtime profile drift inventory rejected");
}
const { profileDigest: _profileDigest, ...manifestBody } = manifest;
if (
!SHA256.test(manifest.profileDigest ?? "") ||
sha256(stableJson(manifestBody)) !== manifest.profileDigest
) {
throw new Error("runtime profile drift manifest digest rejected");
}
const actualFileHashes = Object.fromEntries(
Object.keys(manifest.fileHashes).map((logicalPath) => {
const pathValue = join(target, logicalPath);
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o022) !== 0
) {
throw new Error("runtime profile drift file rejected");
}
return [logicalPath, sha256(readFileSync(pathValue))];
})
);
const runtimeConfig = JSON.parse(
readFileSync(join(target, "config.yaml"), "utf8")
);
const runtimeMcp = JSON.parse(
readFileSync(join(target, "sellable", "mcp.json"), "utf8")
);
const tools = runtimeConfig?.tools?.include;
if (
!Array.isArray(tools) ||
tools.length === 0 ||
tools.some((tool) => typeof tool !== "string")
) {
throw new Error("runtime profile drift tools rejected");
}
const nonSoulFileHashes = Object.fromEntries(
Object.entries(actualFileHashes).filter(
([logicalPath]) => logicalPath !== "SOUL.md"
)
);
return {
manifest,
runtimeConfig,
runtimeMcp,
tools: [...tools].sort(),
fileHashes: actualFileHashes,
profileDigest: sha256(
stableJson({ ...manifestBody, fileHashes: actualFileHashes })
),
nonSoulContentHash: sha256(stableJson(nonSoulFileHashes)),
};
}
function runtimeServiceEnvLocation(profile) {
const servicePath = resolve(profile.runtimeServicePath ?? "");
const envRoot = resolve(profile.runtimeServiceEnvRoot ?? "");
if (
!isAbsolute(servicePath) ||
!existsSync(servicePath) ||
lstatSync(servicePath).isSymbolicLink() ||
!lstatSync(servicePath).isDirectory() ||
(lstatSync(servicePath).mode & 0o022) !== 0 ||
envRoot !== join(servicePath, "env")
)
throw new Error("runtime service environment path rejected");
const realServicePath = realpathSync(servicePath);
if (realpathSync(dirname(envRoot)) !== realServicePath)
throw new Error("runtime service environment parent rejected");
return {
servicePath: realServicePath,
envRoot: join(realServicePath, "env"),
};
}
function observeClosedServiceEnvironment({ envRoot, values }) {
const ownerUid = process.getuid?.() ?? lstatSync(envRoot).uid;
const ownerGid = process.getgid?.() ?? lstatSync(envRoot).gid;
const names = readdirSync(envRoot).sort();
if (JSON.stringify(names) !== JSON.stringify(Object.keys(values).sort()))
throw new Error("runtime service environment inventory rejected");
for (const key of names) {
const pathValue = join(envRoot, key);
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
link.uid !== ownerUid ||
link.gid !== ownerGid ||
(link.mode & 0o777) !== 0o600 ||
readFileSync(pathValue, "utf8") !== `${values[key]}\n`
)
throw new Error("runtime service environment readback rejected");
}
const root = lstatSync(envRoot);
if (
root.isSymbolicLink() ||
!root.isDirectory() ||
root.uid !== ownerUid ||
root.gid !== ownerGid ||
(root.mode & 0o777) !== 0o700
) {
throw new Error("runtime service environment root rejected");
}
return { keys: names, digest: sha256(stableJson(values)), path: envRoot };
}
function writeClosedServiceEnvironment({
envRoot,
servicePath,
request,
values,
}) {
for (const [key, value] of Object.entries(values)) {
if (
!/^[A-Z][A-Z0-9_]{0,127}$/.test(key) ||
typeof value !== "string" ||
!value ||
/[\0\r\n]/.test(value)
) {
throw new Error("runtime service environment value rejected");
}
}
const staging = join(
servicePath,
`.env.staging-${request.operationId}-${request.fence}`
);
const backup = join(servicePath, ".env.rollback-prior-good");
for (const pathValue of [staging, backup]) {
if (
dirname(pathValue) !== servicePath ||
(existsSync(pathValue) && lstatSync(pathValue).isSymbolicLink())
) {
throw new Error("runtime service environment staging rejected");
}
}
if (existsSync(backup) && !existsSync(envRoot)) renameSync(backup, envRoot);
rmSync(staging, { recursive: true, force: true });
rmSync(backup, { recursive: true, force: true });
mkdirSync(staging, { mode: 0o700 });
chmodSync(staging, 0o700);
for (const key of Object.keys(values).sort()) {
const pathValue = join(staging, key);
writeFileSync(pathValue, `${values[key]}\n`, { mode: 0o600, flag: "wx" });
chmodSync(pathValue, 0o600);
const descriptor = openSync(pathValue, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
}
fsyncDirectory(staging);
if (existsSync(envRoot)) renameSync(envRoot, backup);
renameSync(staging, envRoot);
fsyncDirectory(servicePath);
rmSync(backup, { recursive: true, force: true });
fsyncDirectory(servicePath);
return observeClosedServiceEnvironment({ envRoot, values });
}
function runtimeServiceEnvironmentValues({
profile,
profileId,
manifest,
target,
runtimeStateRoot,
}) {
const slackRoot = privateRoot(profile.workerRuntimeSecretRoot);
const mcpRoot = privateRoot(profile.mcpCredentialRoot);
const hermesRoot = runtimeStateRoot;
const hermesProfileRoot = join(hermesRoot, "profiles", profileId);
const runtimeHome = join(hermesProfileRoot, "home");
return {
paths: { slackRoot, mcpRoot, runtimeHome, hermesRoot, hermesProfileRoot },
values: {
HERMES_HOME: hermesRoot,
HOME: runtimeHome,
SELLABLE_AGENT_CREDENTIAL_GENERATION: String(
manifest.serviceCredentialGeneration
),
SELLABLE_AGENT_DEFAULT_CHANNEL_ID: manifest.slackDefaultChannelId,
SELLABLE_AGENT_HERMES_PROFILE_ROOT: hermesProfileRoot,
SELLABLE_AGENT_ID: manifest.agentId,
SELLABLE_AGENT_MANAGED_PROFILE_ROOT: target,
SELLABLE_AGENT_MCP_CREDENTIAL_ROOT: mcpRoot,
SELLABLE_AGENT_MEMBER_ALLOWLIST: manifest.slackMemberAllowlist.join(","),
SELLABLE_AGENT_POLICY_HASH: manifest.policyHash,
SELLABLE_AGENT_PROFILE_ID: profileId,
SELLABLE_AGENT_RUNTIME_ENV_LOADED: "1",
SELLABLE_AGENT_RUNTIME_SECRET_ROOT: mcpRoot,
SELLABLE_AGENT_SELECTED_CHANNEL_IDS: JSON.stringify(
manifest.slackChannelIds
),
SELLABLE_AGENT_SERVICE_CREDENTIAL_FILE: join(
mcpRoot,
profileId,
"service-credential"
),
SELLABLE_AGENT_WORKER_SLACK_SECRET_ROOT: slackRoot,
SELLABLE_AGENT_WORKSPACE_ID: manifest.workspaceId,
SELLABLE_CONFIG_PATH: `/runtime/profiles/${profileId}/sellable/mcp.json`,
SELLABLE_LOCK_WORKSPACE_ID: manifest.workspaceId,
SELLABLE_REQUIRE_WORKSPACE_LOCK: "1",
SLACK_ALLOWED_CHANNELS: manifest.slackChannelIds.join(","),
SLACK_ALLOWED_USERS: manifest.slackMemberAllowlist.join(","),
SLACK_HOME_CHANNEL: manifest.slackDefaultChannelId,
SLACK_REQUIRE_MENTION: "true",
},
};
}
function safePinnedFile(pathValue, label) {
const resolved = resolve(pathValue ?? "");
if (!isAbsolute(resolved) || !existsSync(resolved))
throw new Error(`${label} missing`);
const link = lstatSync(resolved);
if (link.isSymbolicLink() || !link.isFile() || (link.mode & 0o022) !== 0)
throw new Error(`${label} rejected`);
return resolved;
}
function externalTarget(pathRoot, absolutePath) {
const configuredRoot = resolve(pathRoot ?? "/");
const root = existsSync(configuredRoot)
? realpathSync(configuredRoot)
: configuredRoot;
const target =
root === "/"
? resolve(absolutePath)
: join(root, String(absolutePath).replace(/^\/+/, ""));
const confined = relative(root, target);
if (
!isAbsolute(root) ||
!isAbsolute(absolutePath) ||
confined.startsWith("..") ||
isAbsolute(confined)
) {
throw new Error("external runtime path confinement rejected");
}
return target;
}
function observeImmutableDirectory(pathValue, ownerUid, ownerGid) {
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
link.uid !== ownerUid ||
link.gid !== ownerGid ||
(link.mode & 0o022) !== 0 ||
realpathSync(pathValue) !== pathValue
)
throw new Error("external runtime directory rejected");
}
function walkExternalRoot(root, ownerUid, ownerGid) {
const files = [];
const directories = [];
const visit = (directory, logicalDirectory) => {
observeImmutableDirectory(directory, ownerUid, ownerGid);
if (logicalDirectory)
directories.push({
path: logicalDirectory,
mode: lstatSync(directory).mode & 0o777,
});
for (const name of readdirSync(directory).sort()) {
if (
!name ||
name === "." ||
name === ".." ||
name.includes("/") ||
name.includes("\\")
) {
throw new Error("external runtime inventory name rejected");
}
const pathValue = join(directory, name);
const logicalPath = logicalDirectory
? `${logicalDirectory}/${name}`
: name;
const link = lstatSync(pathValue);
if (link.isSymbolicLink())
throw new Error("external runtime symlink rejected");
if (link.isDirectory()) visit(pathValue, logicalPath);
else if (link.isFile()) files.push({ logicalPath, pathValue, link });
else throw new Error("external runtime special file rejected");
}
};
visit(root, "");
const byPath = (left, right) =>
left.path < right.path ? -1 : left.path > right.path ? 1 : 0;
const byLogicalPath = (left, right) =>
left.logicalPath < right.logicalPath
? -1
: left.logicalPath > right.logicalPath
? 1
: 0;
return {
directories: directories.sort(byPath),
files: files.sort(byLogicalPath),
};
}
export function observeExternalRuntimeClosure(input = {}) {
const pathRoot = resolve(input.pathRoot ?? "/");
const ownerUid = input.ownerUid ?? 0;
const ownerGid = input.ownerGid ?? 0;
const manifestHostPath = resolve(
input.manifestPath ?? EXTERNAL_RUNTIME_MANIFEST_PATH
);
const expectedClosureRoot = resolve(
input.expectedClosureRoot ?? EXTERNAL_RUNTIME_CLOSURE_ROOT
);
const trustRoot = resolve(input.trustRoot ?? dirname(expectedClosureRoot));
if (
!Number.isSafeInteger(ownerUid) ||
ownerUid < 0 ||
!Number.isSafeInteger(ownerGid) ||
ownerGid < 0 ||
manifestHostPath !== join(expectedClosureRoot, "manifest.json") ||
relative(trustRoot, expectedClosureRoot).startsWith("..")
)
throw new Error("external runtime closure input rejected");
const mappedTrustRoot = externalTarget(pathRoot, trustRoot);
const mappedClosureRoot = externalTarget(pathRoot, expectedClosureRoot);
const mappedManifestPath = externalTarget(pathRoot, manifestHostPath);
observeImmutableDirectory(mappedTrustRoot, ownerUid, ownerGid);
const trustRelative = relative(mappedTrustRoot, mappedClosureRoot);
let current = mappedTrustRoot;
for (const component of trustRelative.split(/[/\\]+/).filter(Boolean)) {
current = join(current, component);
observeImmutableDirectory(current, ownerUid, ownerGid);
}
const manifestLink = lstatSync(mappedManifestPath);
if (
manifestLink.isSymbolicLink() ||
!manifestLink.isFile() ||
manifestLink.uid !== ownerUid ||
manifestLink.gid !== ownerGid ||
(manifestLink.mode & 0o777) !== 0o444
)
throw new Error("external runtime manifest rejected");
if (
JSON.stringify(readdirSync(mappedClosureRoot).sort()) !==
JSON.stringify(["hermes", "manifest.json", "mcp"])
)
throw new Error("external runtime closure root inventory rejected");
const manifestBytes = readFileSync(mappedManifestPath);
const manifest = JSON.parse(manifestBytes.toString("utf8"));
if (
!exactKeys(manifest, ["version", "closureRoot", "roots"]) ||
manifest.version !== EXTERNAL_RUNTIME_MANIFEST_VERSION ||
manifest.closureRoot !== expectedClosureRoot ||
!Array.isArray(manifest.roots) ||
JSON.stringify(manifest.roots.map((root) => root?.kind)) !==
JSON.stringify(["mcp", "hermes"])
)
throw new Error("external runtime manifest schema rejected");
const observations = [];
for (const rootRecord of manifest.roots) {
if (
!exactKeys(rootRecord, [
"kind",
"entrypoint",
"rootMode",
"directories",
"files",
]) ||
!["mcp", "hermes"].includes(rootRecord.kind) ||
rootRecord.rootMode !== 0o555 ||
typeof rootRecord.entrypoint !== "string" ||
!Array.isArray(rootRecord.directories) ||
!Array.isArray(rootRecord.files)
)
throw new Error("external runtime root schema rejected");
const hostRoot = join(expectedClosureRoot, rootRecord.kind);
const mappedRoot = externalTarget(pathRoot, hostRoot);
if ((lstatSync(mappedRoot).mode & 0o777) !== rootRecord.rootMode) {
throw new Error("external runtime root mode rejected");
}
const walked = walkExternalRoot(mappedRoot, ownerUid, ownerGid);
if (
JSON.stringify(
rootRecord.directories.map((directory) => directory?.path)
) !==
JSON.stringify(
rootRecord.directories.map((directory) => directory?.path).sort()
) ||
new Set(rootRecord.directories.map((directory) => directory?.path))
.size !== rootRecord.directories.length
)
throw new Error("external runtime directory order rejected");
if (
rootRecord.directories.some(
(directory) =>
!exactKeys(directory, ["path", "mode"]) ||
!/^[-A-Za-z0-9@._+]+(?:\/[-A-Za-z0-9@._+]+)*$/.test(
directory.path ?? ""
) ||
directory.mode !== 0o555
)
)
throw new Error("external runtime directory schema rejected");
if (
JSON.stringify(walked.directories) !==
JSON.stringify(rootRecord.directories)
) {
throw new Error("external runtime directory inventory rejected");
}
if (
rootRecord.files.length === 0 ||
JSON.stringify(rootRecord.files.map((file) => file?.path)) !==
JSON.stringify(rootRecord.files.map((file) => file?.path).sort()) ||
new Set(rootRecord.files.map((file) => file?.path)).size !==
rootRecord.files.length
)
throw new Error("external runtime file order rejected");
const actualPaths = walked.files.map((file) => file.logicalPath);
if (
JSON.stringify(actualPaths) !==
JSON.stringify(rootRecord.files.map((file) => file.path))
) {
throw new Error("external runtime open inventory rejected");
}
const observedFiles = rootRecord.files.map((file, index) => {
if (
!exactKeys(file, ["path", "mode", "digest"]) ||
!/^[-A-Za-z0-9@._+]+(?:\/[-A-Za-z0-9@._+]+)*$/.test(file.path ?? "") ||
![0o444, 0o555].includes(file.mode) ||
!SHA256.test(file.digest ?? "")
)
throw new Error("external runtime file schema rejected");
const actual = walked.files[index];
const digest = sha256(readFileSync(actual.pathValue));
if (
actual.link.uid !== ownerUid ||
actual.link.gid !== ownerGid ||
(actual.link.mode & 0o777) !== file.mode ||
digest !== file.digest
)
throw new Error("external runtime file drift rejected");
return { path: file.path, mode: file.mode, digest };
});
if (
!rootRecord.files.some(
(file) => file.path === rootRecord.entrypoint && file.mode === 0o555
)
) {
throw new Error("external runtime entrypoint rejected");
}
observations.push({
kind: rootRecord.kind,
root: hostRoot,
entrypoint: join(hostRoot, rootRecord.entrypoint),
rootMode: rootRecord.rootMode,
directories: [...rootRecord.directories],
files: observedFiles,
});
}
const expectedEntrypoints = input.expectedEntrypoints ?? {
mcp: EXTERNAL_MCP_ENTRYPOINT,
hermes: EXTERNAL_HERMES_ENTRYPOINT,
};
for (const observed of observations) {
if (observed.entrypoint !== expectedEntrypoints[observed.kind]) {
throw new Error("external runtime executed entrypoint rejected");
}
}
return {
version: EXTERNAL_RUNTIME_MANIFEST_VERSION,
manifestDigest: sha256(manifestBytes),
roots: observations,
closureDigest: sha256(
stableJson({ manifest: sha256(manifestBytes), roots: observations })
),
};
}
export function runtimePackageHash(profile) {
const manifestPath = safePinnedFile(
profile.installedPackageManifestPath,
"installed package manifest"
);
const manifestBytes = readFileSync(manifestPath);
const manifest = JSON.parse(manifestBytes.toString("utf8"));
if (
manifest?.version !== "sellable-agent-installed-closure/v1" ||
!Array.isArray(manifest.files)
) {
throw new Error("installed package manifest rejected");
}
const external = observeExternalRuntimeClosure({
manifestPath: profile.externalRuntimeManifestPath,
pathRoot: profile.externalRuntimePathRoot,
trustRoot: profile.externalRuntimeTrustRoot,
expectedClosureRoot: profile.externalRuntimeClosureRoot,
ownerUid: profile.externalRuntimeOwnerUid,
ownerGid: profile.externalRuntimeOwnerGid,
expectedEntrypoints: {
mcp: profile.mcpCommand,
hermes: profile.hermesExecutablePath,
},
});
return sha256(
stableJson({
installedManifest: sha256(manifestBytes),
externalRuntimeClosure: external.closureDigest,
hermesBridge: HERMES_AGENT_BRIDGE_CONTRACT.contractDigest,
})
);
}
function runtimeBoundaryInput({
profile,
profileId,
manifest,
target,
runtimeStateRoot,
}) {
const proxyExecutablePath = safePinnedFile(
profile.proxyExecutablePath,
"proxy executable"
);
const proxyConfigPath = safePinnedFile(
profile.proxyConfigPath,
"proxy config"
);
const hermesExecutablePath = safePinnedFile(
profile.hermesExecutablePath,
"Hermes executable"
);
const proxyConfigBytes = readFileSync(proxyConfigPath);
const proxy = validateRuntimeEgressProxyConfig(
JSON.parse(proxyConfigBytes.toString("utf8"))
);
if (
!proxy.ok ||
proxy.config.uid !== profile.proxyUid ||
proxy.config.gid !== profile.proxyGid ||
proxy.config.listenPort !== profile.proxyPort
)
throw new Error("proxy runtime identity rejected");
const hermesSourceRoot = immutableSourceRoot(profile.hermesSourceRoot);
return {
profileId,
workspaceId: manifest.workspaceId,
agentId: manifest.agentId,
runtimeUid: profile.runtimeUid,
runtimeGid: profile.runtimeGid,
proxyUid: profile.proxyUid,
proxyGid: profile.proxyGid,
proxyPort: profile.proxyPort,
proxyExecutablePath,
proxyExecutableDigest: sha256(readFileSync(proxyExecutablePath)),
proxyConfigPath,
proxyConfigDigest: sha256(proxyConfigBytes),
activeProfileRoot: target,
runtimeStateRoot,
mcpCredentialRoot: privateRoot(profile.mcpCredentialRoot),
hermesSourceRoot,
hermesExecutablePath,
hermesExecutableDigest: sha256(readFileSync(hermesExecutablePath)),
mcpExecutablePath: profile.mcpCommand,
bubblewrapPath: profile.bubblewrapPath,
setprivPath: profile.setprivPath,
nftPath: profile.nftPath,
cgroupRoot: profile.cgroupRoot,
serviceCredentialGeneration: manifest.serviceCredentialGeneration,
policyHash: manifest.policyHash,
selectedChannelIds: [...manifest.slackChannelIds],
defaultChannelId: manifest.slackDefaultChannelId,
memberAllowlist: [...manifest.slackMemberAllowlist],
};
}
function writeRuntimeBoundaryConfig(profile, input) {
const built = buildRuntimeBoundaryContract(input);
if (!built.ok) throw new Error("runtime boundary contract rejected");
const pathValue = resolve(profile.runtimeBoundaryConfigPath ?? "");
if (
!isAbsolute(pathValue) ||
basename(pathValue) !== "runtime-boundary.json"
) {
throw new Error("runtime boundary config path rejected");
}
if (
process.getuid?.() === 0 &&
pathValue !== "/etc/sellable-agent/runtime-boundary.json"
) {
throw new Error("live runtime boundary config path rejected");
}
const parent = dirname(pathValue);
const parentLink = lstatSync(parent);
const ownerUid =
process.getuid?.() === 0 ? 0 : (process.getuid?.() ?? parentLink.uid);
const ownerGid =
process.getuid?.() === 0 ? 0 : (process.getgid?.() ?? parentLink.gid);
if (
parentLink.isSymbolicLink() ||
!parentLink.isDirectory() ||
(parentLink.mode & 0o022) !== 0 ||
parentLink.uid !== ownerUid ||
parentLink.gid !== ownerGid ||
realpathSync(parent) !== parent
)
throw new Error("runtime boundary config parent rejected");
if (existsSync(pathValue) && lstatSync(pathValue).isSymbolicLink())
throw new Error("runtime boundary config symlink rejected");
const bytes = `${JSON.stringify(input, null, 2)}\n`;
const temp = join(parent, `.runtime-boundary.${randomUUID()}.tmp`);
writeFileSync(temp, bytes, { mode: 0o400, flag: "wx" });
chmodSync(temp, 0o400);
const descriptor = openSync(temp, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
renameSync(temp, pathValue);
fsyncDirectory(parent);
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
link.uid !== ownerUid ||
link.gid !== ownerGid ||
(link.mode & 0o777) !== 0o400 ||
readFileSync(pathValue, "utf8") !== bytes
)
throw new Error("runtime boundary config readback rejected");
const readback = buildRuntimeBoundaryContract(
JSON.parse(readFileSync(pathValue, "utf8"))
);
if (
!readback.ok ||
readback.contract.contractDigest !== built.contract.contractDigest
) {
throw new Error("runtime boundary contract readback rejected");
}
return {
path: pathValue,
contractDigest: built.contract.contractDigest,
fileDigest: sha256(bytes),
};
}
function observeRuntimeBoundaryConfig(profile, input) {
const pathValue = resolve(profile.runtimeBoundaryConfigPath ?? "");
const link = lstatSync(pathValue);
const parent = lstatSync(dirname(pathValue));
const ownerUid =
process.getuid?.() === 0 ? 0 : (process.getuid?.() ?? link.uid);
const ownerGid =
process.getuid?.() === 0 ? 0 : (process.getgid?.() ?? link.gid);
if (
parent.isSymbolicLink() ||
!parent.isDirectory() ||
parent.uid !== ownerUid ||
parent.gid !== ownerGid ||
(parent.mode & 0o022) !== 0 ||
realpathSync(dirname(pathValue)) !== dirname(pathValue) ||
link.isSymbolicLink() ||
!link.isFile() ||
link.uid !== ownerUid ||
link.gid !== ownerGid ||
(link.mode & 0o777) !== 0o400
)
throw new Error("runtime boundary config observation rejected");
const parsed = JSON.parse(readFileSync(pathValue, "utf8"));
if (stableJson(parsed) !== stableJson(input))
throw new Error("runtime boundary config drift rejected");
const built = buildRuntimeBoundaryContract(parsed);
if (!built.ok) throw new Error("runtime boundary config contract rejected");
return built.contract;
}
function runtimePidFromCgroup(contract, procRoot) {
const procsPath = join(
contract.paths.cgroupRoot,
contract.profileId,
"cgroup.procs"
);
const candidates = readFileSync(procsPath, "utf8")
.split(/\s+/)
.filter((value) => /^[1-9][0-9]{0,9}$/.test(value));
const matches = candidates.filter((pidText) => {
try {
const status = readFileSync(join(procRoot, pidText, "status"), "utf8");
const uid = Number(status.match(/^Uid:\s+(\d+)/m)?.[1]);
const cmdline = readFileSync(join(procRoot, pidText, "cmdline"))
.toString("utf8")
.split("\0")
.filter(Boolean);
return (
uid === contract.runtime.uid &&
cmdline.includes(contract.paths.hermesExecutablePath)
);
} catch {
return false;
}
});
if (matches.length !== 1) throw new Error("runtime cgroup process rejected");
return Number(matches[0]);
}
function readPinnedPid(pathValue) {
const resolved = resolve(pathValue ?? "");
const link = lstatSync(resolved);
const value = readFileSync(resolved, "utf8").trim();
if (
link.isSymbolicLink() ||
!link.isFile() ||
!/^[1-9][0-9]{0,9}$/.test(value)
) {
throw new Error("supervised pid rejected");
}
return Number(value);
}
function proxyListens(host, port) {
return new Promise((resolveListen) => {
const socket = createConnection({ host, port });
const finish = (value) => {
socket.removeAllListeners();
socket.destroy();
resolveListen(value);
};
socket.setTimeout(2_000, () => finish(false));
socket.once("error", () => finish(false));
socket.once("connect", () => finish(true));
});
}
async function observeKernelRuntimeBoundary(profile, contract, procRoot) {
const runtimePid = runtimePidFromCgroup(contract, procRoot);
const proxyPid = readPinnedPid(profile.proxyPidPath);
const nft = await runBoundedChildProcess({
command: contract.paths.nftPath,
args: ["list", "table", "inet", contract.firewall.table],
env: { PATH: "/usr/sbin:/usr/bin:/sbin:/bin" },
shell: false,
timeoutMs: 10_000,
maxOutputBytes: 128 * 1024,
});
if (
nft.exitCode !== 0 ||
nft.timedOut ||
nft.outputLimitExceeded ||
nft.stderr.trim()
) {
throw new Error("runtime firewall readback failed");
}
const listenObserved = await proxyListens(
contract.proxy.host,
contract.proxy.port
);
const proxyObservation = observeRuntimeEgressProxy({
configPath: contract.proxy.configPath,
executablePath: contract.proxy.executablePath,
pid: proxyPid,
procRoot,
listenObserved,
});
if (!proxyObservation.ok) throw new Error("runtime proxy readback failed");
const boundary = observeRuntimeBoundary({
contract,
pid: runtimePid,
procRoot,
cgroupRoot: contract.paths.cgroupRoot,
nftRuleset: nft.stdout,
proxyObservation,
});
if (!boundary.ok) throw new Error("runtime boundary readback failed");
return { runtimePid, boundary };
}
function installRuntimeServiceEnvironment(profile, request) {
const { target } = profileTarget(profile, { ...request, slot: "ACTIVE" }, [
"ACTIVE",
]);
if (!existsSync(target)) throw new Error("active profile absent");
const manifest = validateActiveManifest(target, request, profile);
const runtimeStateRoot = safeRuntimeStateRoot(profile);
const { paths, values } = runtimeServiceEnvironmentValues({
profile,
profileId: request.profileId,
manifest,
target,
runtimeStateRoot,
});
const { runtimeHome, hermesRoot } = paths;
const { servicePath, envRoot } = runtimeServiceEnvLocation(profile);
const hermesProfilesRoot = join(hermesRoot, "profiles");
ensureOwnedRuntimeDirectory(
hermesProfilesRoot,
hermesRoot,
profile.runtimeUid,
profile.runtimeGid
);
const hermesProfileRoot = join(hermesProfilesRoot, request.profileId);
ensureOwnedRuntimeDirectory(
hermesProfileRoot,
hermesProfilesRoot,
profile.runtimeUid,
profile.runtimeGid
);
ensureOwnedRuntimeDirectory(
runtimeHome,
hermesProfileRoot,
profile.runtimeUid,
profile.runtimeGid
);
const installed = writeClosedServiceEnvironment({
envRoot,
servicePath,
request,
values,
});
const boundaryInput = runtimeBoundaryInput({
profile,
profileId: request.profileId,
manifest,
target,
runtimeStateRoot,
});
const boundary = writeRuntimeBoundaryConfig(profile, boundaryInput);
return {
ok: true,
profileId: request.profileId,
revisionId: request.revisionId,
profileDigest: request.profileDigest,
envDigest: installed.digest,
envKeys: installed.keys,
envPath: installed.path,
boundaryConfigPath: boundary.path,
boundaryContractDigest: boundary.contractDigest,
boundaryFileDigest: boundary.fileDigest,
hermesRoot,
hermesProfileRoot,
runtimeHome,
};
}
function observedSecret(pathValue, uid, gid) {
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o777) !== 0o600 ||
link.uid !== uid ||
link.gid !== gid
)
throw new Error("runtime secret readback rejected");
const secret = readFileSync(pathValue, "utf8").trim();
if (!secret) throw new Error("runtime secret absent");
return sha256(secret).slice(0, 16);
}
async function observeRuntime(profile, request) {
const driftMode = request.action === "OBSERVE_RUNTIME_DRIFT";
if (
!Number.isSafeInteger(profile.workerUid) ||
profile.workerUid < 1 ||
!Number.isSafeInteger(profile.workerGid) ||
profile.workerGid < 1 ||
profile.workerUid === profile.runtimeUid
)
throw new Error("worker profile identity rejected");
const { target } = profileTarget(profile, { ...request, slot: "ACTIVE" }, [
"ACTIVE",
]);
if (!existsSync(target)) throw new Error("active profile absent");
const entries = observeProfileEntries(target);
if (
JSON.stringify(entries.map(({ path, type }) => [path, type]).sort()) !==
JSON.stringify([...PROFILE_ENTRIES].sort()) ||
entries.some(
(entry) =>
entry.uid !== profile.runtimeUid ||
entry.gid !== profile.runtimeGid ||
entry.mode !== (entry.type === "directory" ? 0o500 : 0o400)
)
)
throw new Error("active profile readback rejected");
const manifestCandidate = JSON.parse(
readFileSync(join(target, "agent-profile.json"), "utf8")
);
const manifest = validateActiveManifest(
target,
{
...request,
revisionId: manifestCandidate.revisionId,
profileDigest: manifestCandidate.profileDigest,
},
profile,
driftMode
);
if (manifest.profileId !== request.profileId)
throw new Error("active profile identity rejected");
const runtimeStateRoot = safeRuntimeStateRoot(profile);
const runtimeService = runtimeServiceEnvironmentValues({
profile,
profileId: request.profileId,
manifest,
target,
runtimeStateRoot,
});
for (const pathValue of [
runtimeService.paths.runtimeHome,
runtimeService.paths.hermesRoot,
join(runtimeService.paths.hermesRoot, "profiles"),
runtimeService.paths.hermesProfileRoot,
]) {
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
link.uid !== profile.runtimeUid ||
link.gid !== profile.runtimeGid ||
(link.mode & 0o777) !== 0o700
)
throw new Error("runtime Hermes layout rejected");
}
let environmentDrift = false;
try {
observeClosedServiceEnvironment({
envRoot: runtimeServiceEnvLocation(profile).envRoot,
values: runtimeService.values,
});
} catch (error) {
if (!driftMode) throw error;
environmentDrift = true;
}
const boundaryInput = runtimeBoundaryInput({
profile,
profileId: request.profileId,
manifest,
target,
runtimeStateRoot,
});
const boundaryContract = observeRuntimeBoundaryConfig(profile, boundaryInput);
const procRoot = resolve(profile.procRoot ?? "/proc");
const kernel = await observeKernelRuntimeBoundary(
profile,
boundaryContract,
procRoot
);
const slackRoot = privateRoot(profile.workerRuntimeSecretRoot);
const slackProfileRoot = join(slackRoot, request.profileId);
const mcpRoot = privateRoot(profile.mcpCredentialRoot);
const mcpProfileRoot = join(mcpRoot, request.profileId);
const pidText = String(kernel.runtimePid);
const statText = readFileSync(join(procRoot, pidText, "stat"), "utf8").trim();
const closeParen = statText.lastIndexOf(")");
const fields = statText.slice(closeParen + 2).split(/\s+/);
const startTicks = fields[19];
const status = readFileSync(join(procRoot, pidText, "status"), "utf8");
const uid = Number(status.match(/^Uid:\s+(\d+)/m)?.[1]);
if (
uid !== profile.runtimeUid ||
!/^[1-9][0-9]{0,31}$/.test(startTicks ?? "")
) {
throw new Error("runtime process identity rejected");
}
const bridge = verifyHermesAgentBridge({
sourceRoot: immutableSourceRoot(profile.hermesSourceRoot),
contract: HERMES_AGENT_BRIDGE_CONTRACT,
});
const profileFiles = observeRuntimeProfileFiles(target);
const {
runtimeConfig,
runtimeMcp,
tools,
fileHashes: actualFileHashes,
} = profileFiles;
const actualProfileDigest = profileFiles.profileDigest;
let observedProvider;
try {
observedProvider = observeProviderCredential(profile, request.profileId);
if (observedProvider !== runtimeConfig?.provider?.fingerprint) {
throw new Error("provider credential fingerprint rejected");
}
} catch (error) {
if (!driftMode) throw error;
observedProvider = sha256(
`provider-drift\0${runtimeConfig?.provider?.fingerprint ?? "absent"}`
);
}
if (environmentDrift) {
observedProvider = sha256(`environment-drift\0${observedProvider}`);
}
return {
profileId: manifest.profileId,
workspaceId: manifest.workspaceId,
agentId: manifest.agentId,
revisionId: manifest.revisionId,
revisionSequence: manifest.revisionSequence,
profileDigest: manifest.profileDigest,
tools: [...tools].sort(),
hashes: {
package: runtimePackageHash(profile),
config: actualFileHashes["config.yaml"],
mcp: actualFileHashes["sellable/mcp.json"],
content: actualProfileDigest,
nonSoulContent: profileFiles.nonSoulContentHash,
policy: runtimeMcp?.policyHash,
slackManifest: runtimeConfig?.slack?.manifestHash,
soul: actualFileHashes["SOUL.md"],
skills: actualFileHashes["skills/sellable/SKILL.md"],
provider: observedProvider,
},
slack: {
app: runtimeConfig?.slack?.appHash,
team: runtimeConfig?.slack?.teamHash,
bot: runtimeConfig?.slack?.botHash,
channels: runtimeConfig?.slack?.channelHash,
defaultChannel: runtimeConfig?.slack?.defaultChannelHash,
memberAllowlist: runtimeConfig?.slack?.memberAllowlistHash,
channelIds: runtimeConfig?.slack?.selectedChannelIds,
defaultChannelId: runtimeConfig?.slack?.defaultChannelId,
memberIds: runtimeConfig?.slack?.memberAllowlist,
generation: runtimeConfig?.slack?.generation,
xappPresent: true,
xoxbPresent: true,
xappFingerprint: observedSecret(
join(slackProfileRoot, "slack-app"),
profile.workerUid,
profile.workerGid
),
xoxbFingerprint: observedSecret(
join(slackProfileRoot, "slack-bot"),
profile.workerUid,
profile.workerGid
),
},
serviceCredential: {
present: true,
fingerprint: observedSecret(
join(mcpProfileRoot, "service-credential"),
profile.runtimeUid,
profile.runtimeGid
),
generation: runtimeMcp?.serviceCredential?.generation,
},
process: {
ready: true,
pid: Number(pidText),
startTicks,
bootId: readFileSync(
profile.bootIdPath ?? "/proc/sys/kernel/random/boot_id",
"utf8"
).trim(),
serviceUser: "sellable-agent-runtime",
uid,
},
hermesBridge: bridge,
};
}
async function readableAs(pathValue, uid, gid) {
const result = await runBoundedChildProcess({
command: "/usr/bin/test",
args: ["-r", pathValue],
env: { PATH: "/usr/bin:/bin" },
shell: false,
uid,
gid,
timeoutMs: 5_000,
maxOutputBytes: 1024,
});
return (
result.exitCode === 0 && !result.timedOut && !result.outputLimitExceeded
);
}
async function probeRuntimeContainment(profile, request) {
if (
!Number.isSafeInteger(profile.workerUid) ||
profile.workerUid < 1 ||
!Number.isSafeInteger(profile.workerGid) ||
profile.workerGid < 1 ||
!Number.isSafeInteger(profile.siblingUid) ||
profile.siblingUid < 1 ||
!Number.isSafeInteger(profile.siblingGid) ||
profile.siblingGid < 1 ||
!Number.isSafeInteger(profile.adminUid) ||
profile.adminUid < 1 ||
!Number.isSafeInteger(profile.adminGid) ||
profile.adminGid < 1 ||
new Set([
profile.runtimeUid,
profile.workerUid,
profile.siblingUid,
profile.adminUid,
]).size !== 4
)
throw new Error("containment identities rejected");
const { root, target } = resolveContainmentProfileTarget(profile, request);
if (!existsSync(target)) throw new Error("containment profile rejected");
const ownProbe = join(target, "agent-profile.json");
const siblingRoot = join(
root,
`.containment-sibling-${request.profileId}-${randomUUID()}`
);
const siblingProbe = join(siblingRoot, "probe");
mkdirSync(siblingRoot, { mode: 0o500 });
chownSync(siblingRoot, profile.siblingUid, profile.siblingGid);
writeFileSync(siblingProbe, "sibling\n", { mode: 0o400 });
chownSync(siblingProbe, profile.siblingUid, profile.siblingGid);
try {
const workerSlackRoot = privateRoot(profile.workerRuntimeSecretRoot);
const workerSecret = join(workerSlackRoot, request.profileId, "slack-bot");
const hostProbe = resolve(profile.hostProbePath ?? "");
if (
!isAbsolute(hostProbe) ||
!existsSync(hostProbe) ||
lstatSync(hostProbe).isSymbolicLink()
) {
throw new Error("host probe rejected");
}
const entries = observeProfileEntries(target);
const providerBytesDenied = entries
.filter((entry) => entry.type === "file")
.every(
(entry) =>
!/(?:xox[bap]-|oauth-provider-secret-byte|asec\.v1\.)/i.test(
readFileSync(
entry.path === "." ? target : join(target, entry.path),
"utf8"
)
)
);
if (
profile.runtimeProbePath !==
"/usr/local/bin/sellable-agent-runtime-probe" ||
profile.mcpCommand !== EXTERNAL_MCP_ENTRYPOINT
)
throw new Error("runtime probe pin rejected");
const manifest = JSON.parse(
readFileSync(join(target, "agent-profile.json"), "utf8")
);
const mcpRoot = privateRoot(profile.mcpCredentialRoot);
const runtimeStateRoot = privateRoot(profile.runtimeStateRoot);
const probe = await runBoundedChildProcess({
command: profile.runtimeProbePath,
args: ["--profile-id", request.profileId],
env: {
PATH: "/usr/local/bin:/usr/bin:/bin",
HOME: runtimeStateRoot,
NODE_ENV: "production",
SELLABLE_AGENT_MCP_COMMAND: profile.mcpCommand,
SELLABLE_CONFIG_PATH: join(target, "sellable", "mcp.json"),
SELLABLE_AGENT_RUNTIME_SECRET_ROOT: mcpRoot,
SELLABLE_AGENT_PROFILE_ID: request.profileId,
SELLABLE_AGENT_SERVICE_CREDENTIAL_FILE: join(
mcpRoot,
request.profileId,
"service-credential"
),
SELLABLE_AGENT_WORKSPACE_ID: manifest.workspaceId,
SELLABLE_AGENT_ID: manifest.agentId,
SELLABLE_AGENT_CREDENTIAL_GENERATION: String(
manifest.serviceCredentialGeneration
),
SELLABLE_AGENT_POLICY_HASH: manifest.policyHash,
SELLABLE_AGENT_SELECTED_CHANNEL_IDS: JSON.stringify(
manifest.slackChannelIds
),
},
shell: false,
uid: profile.runtimeUid,
gid: profile.runtimeGid,
timeoutMs: 45_000,
maxOutputBytes: 32 * 1024,
});
if (
probe.exitCode !== 0 ||
probe.timedOut ||
probe.outputLimitExceeded ||
probe.stderr.trim()
) {
throw new Error("runtime MCP probe failed");
}
const mcpProof = JSON.parse(probe.stdout);
if (
mcpProof?.ok !== true ||
mcpProof.profileId !== request.profileId ||
mcpProof.workspaceId !== manifest.workspaceId ||
mcpProof.safeRead !== true ||
mcpProof.unexpectedToolDenied !== true ||
mcpProof.wrongChannelDenied !== true ||
mcpProof.wrongWorkspaceDenied !== true ||
JSON.stringify(mcpProof.tools) !==
JSON.stringify([...manifest.toolInclude].sort())
)
throw new Error("runtime MCP proof rejected");
return {
ok: true,
runtimeUid: profile.runtimeUid,
runtimeProfileRead: await readableAs(
ownProbe,
profile.runtimeUid,
profile.runtimeGid
),
siblingReadDenied: !(await readableAs(
ownProbe,
profile.siblingUid,
profile.siblingGid
)),
workerReadDenied: !(await readableAs(
ownProbe,
profile.workerUid,
profile.workerGid
)),
adminReadDenied: !(await readableAs(
ownProbe,
profile.adminUid,
profile.adminGid
)),
runtimeSiblingReadDenied: !(await readableAs(
siblingProbe,
profile.runtimeUid,
profile.runtimeGid
)),
runtimeSlackPathDenied: !(await readableAs(
workerSecret,
profile.runtimeUid,
profile.runtimeGid
)),
providerBytesDenied,
hostMountDenied: !(await readableAs(
hostProbe,
profile.runtimeUid,
profile.runtimeGid
)),
dockerDenied: !(await readableAs(
profile.dockerSocketPath ?? "/var/run/docker.sock",
profile.runtimeUid,
profile.runtimeGid
)),
unexpectedToolDenied: mcpProof.unexpectedToolDenied,
wrongWorkspaceDispatchDenied:
mcpProof.wrongWorkspaceDenied && mcpProof.wrongChannelDenied,
tools: mcpProof.tools,
safeRead: mcpProof.safeRead,
workspaceId: mcpProof.workspaceId,
};
} finally {
rmSync(siblingRoot, { recursive: true, force: true });
fsyncDirectory(root);
}
}
export async function runRuntimeHelperRequest({ requestPath, config }) {
try {
const request = confinedRequest(requestPath, config);
if (!ACTIONS.has(request.action) || !SAFE_ID.test(request.profileId ?? ""))
return { ok: false, code: "helper_action_rejected" };
if (
Object.keys(request).sort().join("\0") !==
[...ACTION_KEYS[request.action]].sort().join("\0") ||
!SAFE_ID.test(request.operationId ?? "") ||
!/^[1-9][0-9]{0,19}$/.test(request.fence ?? "")
)
return { ok: false, code: "helper_request_rejected" };
const profile = config.profiles?.[request.profileId];
if (
!profile ||
!Number.isSafeInteger(profile.runtimeUid) ||
profile.runtimeUid < 1 ||
!Number.isSafeInteger(profile.runtimeGid) ||
profile.runtimeGid < 1
)
return { ok: false, code: "helper_profile_rejected" };
if (request.action === "INSTALL_MCP_CREDENTIAL") {
const sourceRoot = privateRoot(config.workerRequestRoot);
const payload = JSON.parse(
readPrivateDirectFile(sourceRoot, request.sourceRequestFile)
);
if (
payload.operationId !== request.operationId ||
payload.fence !== request.fence ||
payload.mcpServiceGeneration !== request.generation ||
typeof payload.serviceCredential !== "string" ||
!payload.serviceCredential.startsWith("sat_") ||
!SHA256_16.test(request.fingerprint ?? "") ||
createHash("sha256")
.update(payload.serviceCredential)
.digest("hex")
.slice(0, 16) !== request.fingerprint ||
Object.hasOwn(request, "runtimeUid") ||
Object.hasOwn(request, "runtimeGid")
)
throw new Error("credential binding rejected");
const root = privateRoot(profile.mcpCredentialRoot);
const rootOwner = lstatSync(root);
if (
rootOwner.uid !== profile.runtimeUid ||
rootOwner.gid !== profile.runtimeGid
) {
chownSync(root, profile.runtimeUid, profile.runtimeGid);
}
const profileRoot = join(root, request.profileId);
if (dirname(profileRoot) !== root)
throw new Error("credential target rejected");
if (existsSync(profileRoot))
rmSync(profileRoot, { recursive: true, force: true });
mkdirSync(profileRoot, { mode: 0o700 });
const profileOwner = lstatSync(profileRoot);
if (
profileOwner.uid !== profile.runtimeUid ||
profileOwner.gid !== profile.runtimeGid
) {
chownSync(profileRoot, profile.runtimeUid, profile.runtimeGid);
}
const credentialPath = join(profileRoot, "service-credential");
atomicCredential(
credentialPath,
payload.serviceCredential,
profile.runtimeUid,
profile.runtimeGid
);
fsyncDirectory(root);
return {
ok: true,
profileId: request.profileId,
path: credentialPath,
fingerprint: request.fingerprint,
generation: request.generation,
};
}
if (request.action === "INSTALL_PROVIDER_CREDENTIAL") {
return installProviderCredential(profile, request, config);
}
if (request.action === "INSTALL_RUNTIME_SERVICE_ENV") {
return installRuntimeServiceEnvironment(profile, request);
}
if (request.action === "CLEAN_PROFILE_SECRETS") {
for (const configured of [
profile.workerRuntimeSecretRoot,
profile.mcpCredentialRoot,
]) {
const root = privateRoot(configured);
const target = join(root, request.profileId);
if (
dirname(target) !== root ||
(existsSync(target) && lstatSync(target).isSymbolicLink())
)
throw new Error("secret cleanup rejected");
rmSync(target, { recursive: true, force: true });
fsyncDirectory(root);
}
removeProviderCredential(profile, request.profileId);
return { ok: true, profileId: request.profileId, disposition: "REMOVED" };
}
if (request.action === "CHOWN_PROFILE") {
const { root, target } = profileTarget(
profile,
{ ...request, slot: "STAGING" },
["STAGING"]
);
if (
resolve(request.profileRoot ?? "") !== target ||
!existsSync(target) ||
!SHA256.test(request.profileDigest ?? "")
) {
throw new Error("profile ownership target rejected");
}
const before = observeProfileEntries(target);
if (
JSON.stringify(before.map(({ path, type }) => [path, type]).sort()) !==
JSON.stringify([...PROFILE_ENTRIES].sort()) ||
before.some((entry) => (entry.mode & 0o022) !== 0)
)
throw new Error("profile inventory rejected");
const manifest = JSON.parse(
readFileSync(join(target, "agent-profile.json"), "utf8")
);
if (
manifest.profileId !== request.profileId ||
manifest.revisionId !== request.revisionId ||
manifest.profileDigest !== request.profileDigest
)
throw new Error("profile digest binding rejected");
applyRuntimeOwnership(target, profile.runtimeUid, profile.runtimeGid);
fsyncDirectory(root);
const entries = observeProfileEntries(target);
if (
entries.some(
(entry) =>
entry.uid !== profile.runtimeUid ||
entry.gid !== profile.runtimeGid ||
entry.mode !== (entry.type === "directory" ? 0o500 : 0o400)
)
)
throw new Error("profile ownership readback rejected");
return {
ok: true,
profileId: request.profileId,
revisionId: request.revisionId,
profileDigest: request.profileDigest,
entries,
};
}
if (request.action === "REMOVE_PROFILE") {
const { root, target } = profileTarget(profile, request, [
"ACTIVE",
"BACKUP",
"STAGING",
]);
if (existsSync(target)) {
if (lstatSync(target).isSymbolicLink())
throw new Error("profile cleanup rejected");
rmSync(target, { recursive: true, force: true });
fsyncDirectory(root);
}
return {
ok: true,
profileId: request.profileId,
slot: request.slot,
disposition: "REMOVED",
};
}
if (["OBSERVE_RUNTIME", "OBSERVE_RUNTIME_DRIFT"].includes(request.action)) {
return { ok: true, observation: await observeRuntime(profile, request) };
}
if (request.action === "OBSERVE_RUNTIME_LIFECYCLE") {
const { target } = profileTarget(
profile,
{ ...request, slot: "ACTIVE" },
["ACTIVE"]
);
const manifest = JSON.parse(
readFileSync(join(target, "agent-profile.json"), "utf8")
);
const slackProfileRoot = join(
privateRoot(profile.workerRuntimeSecretRoot),
request.profileId
);
const mcpProfileRoot = join(
privateRoot(profile.mcpCredentialRoot),
request.profileId
);
const secretsPresent = [
join(slackProfileRoot, "slack-app"),
join(slackProfileRoot, "slack-bot"),
join(mcpProfileRoot, "service-credential"),
].every(
(pathValue) => existsSync(pathValue) && lstatSync(pathValue).isFile()
);
if (request.lifecycle === "ON") {
const observation = await observeRuntime(profile, request);
if (!secretsPresent) throw new Error("running runtime secrets absent");
return {
ok: true,
lifecycle: "ON",
revisionId: observation.revisionId,
credentialGeneration: observation.serviceCredential.generation,
profileDigest: observation.profileDigest,
secretsPresent,
process: observation.process,
runtimeObservation: observation,
};
}
const servicePath = resolve(profile.runtimeServicePath);
const superviseRoot = join(servicePath, "supervise");
const stat = readFileSync(join(superviseRoot, "stat"), "utf8").trim();
const pid = readFileSync(join(superviseRoot, "pid"), "utf8").trim();
const cgroupProcs = join(
profile.cgroupRoot,
request.profileId,
"cgroup.procs"
);
if (
stat !== "down" ||
!["", "0"].includes(pid) ||
(existsSync(cgroupProcs) && readFileSync(cgroupProcs, "utf8").trim())
) {
throw new Error("stopped runtime process still present");
}
if (["OFF", "ARCHIVING"].includes(request.lifecycle) && !secretsPresent) {
throw new Error("stopped runtime secrets absent");
}
if (request.lifecycle === "ARCHIVED" && secretsPresent)
throw new Error("archived runtime secrets present");
return {
ok: true,
lifecycle: request.lifecycle,
revisionId: manifest.revisionId,
credentialGeneration: manifest.serviceCredentialGeneration,
profileDigest: manifest.profileDigest,
secretsPresent,
process: {
ready: false,
pid: null,
startTicks: null,
bootId: readFileSync(
profile.bootIdPath ?? "/proc/sys/kernel/random/boot_id",
"utf8"
).trim(),
serviceUser: "sellable-agent-runtime",
uid: profile.runtimeUid,
},
};
}
if (request.action === "PROBE_CONTAINMENT") {
return probeRuntimeContainment(profile, request);
}
if (request.action === "INSTALL_HERMES_BRIDGE") {
return {
ok: true,
bridge: verifyHermesAgentBridge({
sourceRoot: immutableSourceRoot(profile.hermesSourceRoot),
contract: HERMES_AGENT_BRIDGE_CONTRACT,
}),
};
}
if (request.action === "VERIFY_HERMES_BRIDGE") {
return {
ok: true,
bridge: verifyHermesAgentBridge({
sourceRoot: immutableSourceRoot(profile.hermesSourceRoot),
contract: HERMES_AGENT_BRIDGE_CONTRACT,
}),
};
}
const command = config.s6SvcPath ?? "/command/s6-svc";
if (
!["/command/s6-svc", "/usr/bin/s6-svc"].includes(command) ||
!["UP", "DOWN", "RESTART"].includes(request.control)
) {
throw new Error("runtime control rejected");
}
const servicePath = resolve(profile.runtimeServicePath);
if (
!isAbsolute(servicePath) ||
!existsSync(servicePath) ||
lstatSync(servicePath).isSymbolicLink()
)
throw new Error("service path rejected");
const result = await runBoundedChildProcess({
command,
args: [
{ UP: "-u", DOWN: "-d", RESTART: "-t" }[request.control],
servicePath,
],
env: { PATH: "/command:/usr/bin:/bin" },
shell: false,
timeoutMs: 15_000,
maxOutputBytes: 16 * 1024,
});
return result.exitCode === 0 &&
!result.timedOut &&
!result.outputLimitExceeded
? { ok: true, profileId: request.profileId, control: request.control }
: { ok: false, code: "runtime_control_failed" };
} catch {
return { ok: false, code: "runtime_helper_rejected" };
}
}