@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
3,295 lines • 108 kB
JavaScript
import { createHash, randomUUID } from "node:crypto";
import {
chmodSync,
chownSync,
closeSync,
existsSync,
fsyncSync,
lstatSync,
mkdirSync,
openSync,
readFileSync,
readdirSync,
realpathSync,
renameSync,
rm,
rmSync,
statfsSync,
writeFileSync,
} from "node:fs";
import {
basename,
dirname,
isAbsolute,
join,
relative,
resolve,
} from "node:path";
import { parseDocument } from "yaml";
import {
SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER,
SELLABLE_DEFAULT_SLACK_TOOLSETS,
sellableSlackToolsetsForMaterialization,
} from "./fly-capabilities-bridge.mjs";
import {
FLY_CUSTOMER_MODEL_ID,
FLY_CUSTOMER_MODEL_PROVIDER,
FLY_CUSTOMER_MODEL_REASONING_EFFORT,
} from "./fly-customer-model.mjs";
import { withProfileSoulLock } from "./fly-soul-bridge.mjs";
import { HERMES_AGENT_BRIDGE_CONTRACT } from "./hermes-bridge.mjs";
import {
HERMES_SNAPSHOT_HOOKS,
HERMES_SNAPSHOT_HOOK_ALLOWLIST_BYTES,
applyHermesSellableHostedPolicy,
isHermesTransientProfilePath,
linkHermesState,
mergeHermesState,
} from "./hermes-memory-snapshot.mjs";
import { createMcpRuntimeBindingV2 } from "./mcp-runtime-binding.mjs";
export const PROFILE_MATERIALIZER_PORTS = Object.freeze({
hostAppSecret: ["resolveHostSecret"],
workspaceBotSecret: ["decryptWorkspaceSecret"],
credential: ["install", "cleanup"],
gateway: ["start", "stop", "observe"],
containment: ["probe"],
mcp: ["discoverTools", "safeRead"],
ownership: ["apply", "observe", "remove"],
});
export const AGENT_PROFILE_INVENTORY = Object.freeze([
"agent-profile.json",
"config.yaml",
"sellable/mcp.json",
"SOUL.md",
"skills/sellable/SKILL.md",
]);
const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
const MODEL_AUTH_ACTIVATION_ID = /^[A-Za-z0-9_.:-]{1,256}$/;
const SHA256 = /^[a-f0-9]{64}$/;
const FINGERPRINT = /^[a-f0-9]{16}$/;
const PRIVATE_REFERENCE = /^(?:secret|oauth-private):\/\/[A-Za-z0-9/_-]+$/;
const RAW_SECRET =
/(?:^|[^A-Za-z0-9])(?:xapp|xoxb|xoxa|sat)_[A-Za-z0-9_-]+|(?:xapp|xoxb|xoxa)-[A-Za-z0-9_-]+|oauth-provider-secret-byte|asec\.v1\./i;
const NATIVE_SLACK_MATERIALIZATION_MARGIN_BYTES = 64 * 1024 * 1024;
export const SELLABLE_INTERNAL_AGENT_WORKSPACE_ID = "cmj0wv9pe02jx23xdv6wfhg9x";
const PRESSURE_CACHE_PATHS = Object.freeze([
[".npm", "_npx"],
[".npm", "_cacache"],
[".cache", "uv"],
[".cache", "pip"],
]);
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 filesystemAvailableBytes(pathValue) {
const stats = statfsSync(pathValue);
return Number(stats.bavail) * Number(stats.bsize);
}
function removeWithoutFollowing(pathValue) {
if (!existsSync(pathValue)) return false;
const entry = lstatSync(pathValue);
rmSync(pathValue, {
recursive: entry.isDirectory() && !entry.isSymbolicLink(),
force: true,
});
return true;
}
function scheduleNativeSlackTreeRemoval(root, target) {
if (dirname(target) !== root || basename(target) === "") {
throw new Error("native_slack_profile_cleanup_path_rejected");
}
let entry;
try {
entry = lstatSync(target);
} catch (error) {
if (error?.code === "ENOENT") return false;
throw error;
}
if (entry.isSymbolicLink() || !entry.isDirectory()) {
throw new Error("native_slack_profile_cleanup_entry_rejected");
}
rm(target, { recursive: true, force: true }, (error) => {
if (error && error.code !== "ENOENT") {
process.stderr.write(
`${JSON.stringify({
ok: false,
code: "native_slack_profile_cleanup_failed",
entry: basename(target),
})}\n`
);
}
});
return true;
}
function sweepStaleNativeSlackTrees(root, profileId) {
const prefix = `.${profileId}.`;
let scheduled = 0;
for (const name of readdirSync(root)) {
if (!name.startsWith(prefix)) continue;
const staleRoot = join(root, name);
let cleanupRoot = staleRoot;
if (name.endsWith(".staging")) {
cleanupRoot = join(root, `.${profileId}.${randomUUID()}.cleanup`);
try {
renameSync(staleRoot, cleanupRoot);
} catch (error) {
// A cleanup scheduled by the prior operation may still be finishing
// after the profile lock is released. Treat that ENOENT as complete.
if (error?.code === "ENOENT") continue;
throw error;
}
} else if (!name.endsWith(".cleanup")) {
continue;
}
if (scheduleNativeSlackTreeRemoval(root, cleanupRoot)) scheduled += 1;
}
return scheduled;
}
function prunePressureCaches(root) {
if (basename(root) !== "profiles") return [];
const dataRoot = dirname(root);
const removed = [];
for (const segments of PRESSURE_CACHE_PATHS) {
const target = join(dataRoot, ...segments);
if (removeWithoutFollowing(target)) removed.push(target);
}
return removed;
}
function materializationCauseCode(error) {
if (error && typeof error === "object") {
const code = error.code;
if (typeof code === "string" && /^[A-Za-z0-9_.:-]{1,128}$/.test(code)) {
return code;
}
}
if (error instanceof Error) {
const code = error.message.split(/[:\s]/, 1)[0];
if (/^[A-Za-z0-9_.-]{1,128}$/.test(code)) return code;
}
return "native_slack_profile_unknown_failure";
}
function exactKeys(value, keys) {
return (
value &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.keys(value).sort().join("\0") === [...keys].sort().join("\0")
);
}
function isInside(root, target) {
const path = relative(root, target);
return path === "" || (!path.startsWith("..") && !isAbsolute(path));
}
function privateRoot(pathValue) {
try {
if (!isAbsolute(pathValue) || !existsSync(pathValue)) return null;
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(link.mode & 0o077) !== 0
)
return null;
return realpathSync(pathValue);
} catch {
return null;
}
}
function traversableProfilesRoot(pathValue) {
try {
if (!isAbsolute(pathValue) || !existsSync(pathValue)) return null;
const link = lstatSync(pathValue);
const mode = link.mode & 0o777;
if (
link.isSymbolicLink() ||
!link.isDirectory() ||
(mode & 0o700) !== 0o700 ||
(mode & 0o066) !== 0
)
return null;
return realpathSync(pathValue);
} catch {
return null;
}
}
export function deriveContainedProfileId(workspaceId, agentId, hostId) {
return `agent-${createHash("sha256")
.update(`profile\0${workspaceId}\0${agentId}\0${hostId}`)
.digest("hex")
.slice(0, 24)}`;
}
const DESIRED_KEYS = [
"workspaceId",
"agentId",
"hostId",
"workerId",
"installationId",
"slackGeneration",
"fence",
"revisionId",
"revisionSequence",
"hermesCli",
"hermesVersion",
"installerPackage",
"mcpPackage",
"workspaceLockId",
"toolInclude",
"policyHash",
"slackManifestHash",
"slackAppHash",
"slackTeamHash",
"slackBotHash",
"slackChannelHash",
"slackDefaultChannelHash",
"slackMemberAllowlistHash",
"slackChannelIds",
"slackDefaultChannelId",
"slackMemberAllowlist",
"soul",
"soulHash",
"skill",
"skillsHash",
"providerReference",
"providerFingerprint",
"serviceCredentialReference",
"serviceCredentialFingerprint",
"serviceCredentialGeneration",
"xappReference",
"xappFingerprint",
"xoxbReference",
"xoxbCiphertextFingerprint",
];
function validateDesired(desired) {
if (!exactKeys(desired, DESIRED_KEYS))
return { ok: false, code: "desired_schema_invalid" };
for (const key of [
"workspaceId",
"agentId",
"hostId",
"workerId",
"installationId",
"revisionId",
]) {
if (!SAFE_ID.test(desired[key]))
return { ok: false, code: "desired_identity_invalid", field: key };
}
if (
desired.workspaceLockId !== desired.workspaceId ||
!/^[1-9][0-9]{0,19}$/.test(desired.fence) ||
!Number.isSafeInteger(desired.revisionSequence) ||
desired.revisionSequence < 1 ||
!Number.isSafeInteger(desired.slackGeneration) ||
desired.slackGeneration < 1 ||
!Number.isSafeInteger(desired.serviceCredentialGeneration) ||
desired.serviceCredentialGeneration < 1 ||
desired.hermesCli !== "hermes" ||
desired.hermesVersion !== "0.18.0" ||
desired.installerPackage !== "@sellable/install@0.1.804" ||
desired.mcpPackage !== "@sellable/mcp@0.1.957" ||
!Array.isArray(desired.toolInclude) ||
desired.toolInclude.length === 0 ||
desired.toolInclude.some((tool) => !/^[a-z][a-z0-9_]{0,127}$/.test(tool)) ||
new Set(desired.toolInclude).size !== desired.toolInclude.length ||
JSON.stringify([...desired.toolInclude].sort()) !==
JSON.stringify(desired.toolInclude) ||
!Array.isArray(desired.slackChannelIds) ||
desired.slackChannelIds.length === 0 ||
desired.slackChannelIds.some((id) => !/^C[A-Z0-9]{8,20}$/.test(id)) ||
JSON.stringify([...new Set(desired.slackChannelIds)].sort()) !==
JSON.stringify(desired.slackChannelIds) ||
!desired.slackChannelIds.includes(desired.slackDefaultChannelId) ||
!Array.isArray(desired.slackMemberAllowlist) ||
desired.slackMemberAllowlist.length === 0 ||
desired.slackMemberAllowlist.some(
(id) => !/^[UW][A-Z0-9]{8,20}$/.test(id)
) ||
JSON.stringify([...new Set(desired.slackMemberAllowlist)].sort()) !==
JSON.stringify(desired.slackMemberAllowlist)
) {
return { ok: false, code: "desired_runtime_invalid" };
}
for (const key of [
"policyHash",
"slackManifestHash",
"slackAppHash",
"slackTeamHash",
"slackBotHash",
"slackChannelHash",
"slackDefaultChannelHash",
"slackMemberAllowlistHash",
"soulHash",
"skillsHash",
"providerFingerprint",
]) {
if (!SHA256.test(desired[key]))
return { ok: false, code: "desired_hash_invalid", field: key };
}
if (
sha256(desired.soul) !== desired.soulHash ||
sha256(desired.skill) !== desired.skillsHash ||
sha256(stableJson(desired.slackChannelIds)) !== desired.slackChannelHash ||
sha256(desired.slackDefaultChannelId) !== desired.slackDefaultChannelHash ||
sha256(stableJson(desired.slackMemberAllowlist)) !==
desired.slackMemberAllowlistHash ||
!PRIVATE_REFERENCE.test(desired.providerReference) ||
!PRIVATE_REFERENCE.test(desired.serviceCredentialReference) ||
!PRIVATE_REFERENCE.test(desired.xappReference) ||
!PRIVATE_REFERENCE.test(desired.xoxbReference) ||
!FINGERPRINT.test(desired.serviceCredentialFingerprint) ||
!FINGERPRINT.test(desired.xappFingerprint) ||
!FINGERPRINT.test(desired.xoxbCiphertextFingerprint) ||
RAW_SECRET.test(desired.soul) ||
RAW_SECRET.test(desired.skill)
) {
return { ok: false, code: "desired_secret_or_content_invalid" };
}
return { ok: true };
}
const MANIFEST_KEYS = [
"schemaVersion",
"producer",
"subject",
"profileId",
"workspaceId",
"agentId",
"hostId",
"workerId",
"installationId",
"slackGeneration",
"fence",
"revisionId",
"revisionSequence",
"hermesCli",
"hermesVersion",
"installerPackage",
"mcpPackage",
"workspaceLockId",
"toolInclude",
"policyHash",
"slackManifestHash",
"slackAppHash",
"slackTeamHash",
"slackBotHash",
"slackChannelHash",
"slackDefaultChannelHash",
"slackMemberAllowlistHash",
"soulHash",
"skillsHash",
"providerReference",
"slackChannelIds",
"slackDefaultChannelId",
"slackMemberAllowlist",
"providerFingerprint",
"serviceCredentialReference",
"serviceCredentialFingerprint",
"serviceCredentialGeneration",
"xappReference",
"xappFingerprint",
"xoxbReference",
"xoxbCiphertextFingerprint",
"fileHashes",
"profileDigest",
];
function manifestBody(desired) {
const profileId = deriveContainedProfileId(
desired.workspaceId,
desired.agentId,
desired.hostId
);
return {
schemaVersion: "sellable-agent-profile/v1",
producer: "sellable-agent-profile-materializer",
subject: `agent-profile:${profileId}`,
profileId,
workspaceId: desired.workspaceId,
agentId: desired.agentId,
hostId: desired.hostId,
workerId: desired.workerId,
installationId: desired.installationId,
slackGeneration: desired.slackGeneration,
fence: desired.fence,
revisionId: desired.revisionId,
revisionSequence: desired.revisionSequence,
hermesCli: desired.hermesCli,
hermesVersion: desired.hermesVersion,
installerPackage: desired.installerPackage,
mcpPackage: desired.mcpPackage,
workspaceLockId: desired.workspaceLockId,
toolInclude: [...desired.toolInclude],
policyHash: desired.policyHash,
slackManifestHash: desired.slackManifestHash,
slackAppHash: desired.slackAppHash,
slackTeamHash: desired.slackTeamHash,
slackBotHash: desired.slackBotHash,
slackChannelHash: desired.slackChannelHash,
slackDefaultChannelHash: desired.slackDefaultChannelHash,
slackMemberAllowlistHash: desired.slackMemberAllowlistHash,
slackChannelIds: [...desired.slackChannelIds],
slackDefaultChannelId: desired.slackDefaultChannelId,
slackMemberAllowlist: [...desired.slackMemberAllowlist],
soulHash: desired.soulHash,
skillsHash: desired.skillsHash,
providerReference: desired.providerReference,
providerFingerprint: desired.providerFingerprint,
serviceCredentialReference: desired.serviceCredentialReference,
serviceCredentialFingerprint: desired.serviceCredentialFingerprint,
serviceCredentialGeneration: desired.serviceCredentialGeneration,
xappReference: desired.xappReference,
xappFingerprint: desired.xappFingerprint,
xoxbReference: desired.xoxbReference,
xoxbCiphertextFingerprint: desired.xoxbCiphertextFingerprint,
};
}
export function buildAgentProfileManifest(desired) {
const valid = validateDesired(desired);
if (!valid.ok) return valid;
return { ok: true, manifest: manifestBody(desired) };
}
function configFor(desired, profileId) {
return {
profile: profileId,
hermes: { cli: desired.hermesCli, version: desired.hermesVersion },
slack: {
appTokenFile: `/run/sellable-agent/${profileId}/slack-app`,
botTokenFile: `/run/sellable-agent/${profileId}/slack-bot`,
generation: desired.slackGeneration,
manifestHash: desired.slackManifestHash,
appHash: desired.slackAppHash,
teamHash: desired.slackTeamHash,
botHash: desired.slackBotHash,
channelHash: desired.slackChannelHash,
defaultChannelHash: desired.slackDefaultChannelHash,
memberAllowlistHash: desired.slackMemberAllowlistHash,
selectedChannelIds: [...desired.slackChannelIds],
defaultChannelId: desired.slackDefaultChannelId,
memberAllowlist: [...desired.slackMemberAllowlist],
},
mcp_servers: {
sellable: {
command:
"/usr/local/lib/sellable-agent/external-runtime/mcp/bin/sellable-mcp",
args: [],
enabled: true,
env: {
SELLABLE_CONFIG_PATH: `/runtime/profiles/${profileId}/sellable/mcp.json`,
SELLABLE_LOCK_WORKSPACE_ID: desired.workspaceId,
SELLABLE_REQUIRE_WORKSPACE_LOCK: "1",
},
},
},
tools: { include: [...desired.toolInclude] },
provider: {
reference: desired.providerReference,
fingerprint: desired.providerFingerprint,
},
};
}
const LIVE_SKILLS_CONFIG_KEYS = Object.freeze([
"disabled",
"platform_disabled",
]);
const LIVE_PLUGIN_CONFIG_KEYS = Object.freeze(["enabled", "disabled"]);
const LIVE_MUTABLE_STATE_ROOTS = Object.freeze([
"skills",
"plugins",
"plugin-data",
]);
function profileConfigWithoutLiveState(value) {
const normalized = structuredClone(value);
if (
normalized?.skills &&
typeof normalized.skills === "object" &&
!Array.isArray(normalized.skills)
) {
for (const key of LIVE_SKILLS_CONFIG_KEYS) delete normalized.skills[key];
if (Object.keys(normalized.skills).length === 0) delete normalized.skills;
}
if (
normalized?.platform_toolsets &&
typeof normalized.platform_toolsets === "object" &&
!Array.isArray(normalized.platform_toolsets)
) {
delete normalized.platform_toolsets.slack;
if (Object.keys(normalized.platform_toolsets).length === 0)
delete normalized.platform_toolsets;
}
if (
normalized?.plugins &&
typeof normalized.plugins === "object" &&
!Array.isArray(normalized.plugins)
) {
for (const key of LIVE_PLUGIN_CONFIG_KEYS) delete normalized.plugins[key];
if (Object.keys(normalized.plugins).length === 0) delete normalized.plugins;
}
return normalized;
}
function readProfileConfig(pathValue) {
const document = parseDocument(readFileSync(pathValue, "utf8"));
const value = document.toJS();
if (
document.errors.length > 0 ||
!value ||
typeof value !== "object" ||
Array.isArray(value)
) {
throw new Error("profile config rejected");
}
return value;
}
function normalizedProfileConfigBytes(pathValue) {
return normalizedProfileConfigValueBytes(readProfileConfig(pathValue));
}
function normalizedProfileConfigValueBytes(value) {
return Buffer.from(
`${JSON.stringify(profileConfigWithoutLiveState(value), null, 2)}\n`,
"utf8"
);
}
function preservedSlackToolsets(
config,
profileRoot,
{ normalizeLegacyNativeDefault = false } = {}
) {
const platformToolsets = config?.platform_toolsets;
const nativeSlackProfile = existsSync(
join(profileRoot, ".no-bundled-skills")
);
if (
!nativeSlackProfile &&
(!platformToolsets ||
typeof platformToolsets !== "object" ||
Array.isArray(platformToolsets) ||
!Object.prototype.hasOwnProperty.call(platformToolsets, "slack"))
)
return normalizeLegacyNativeDefault
? sellableSlackToolsetsForMaterialization(config)
: null;
return sellableSlackToolsetsForMaterialization(config, {
// Only native Slack profiles adopt the one-time browser default. The
// contained legacy profile keeps its existing policy unchanged.
browserDefaultApplied:
!nativeSlackProfile ||
existsSync(join(profileRoot, SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER)),
});
}
function mergeLiveMutableConfig(sourceRoot, targetRoot) {
const sourcePath = join(sourceRoot, "config.yaml");
const targetPath = join(targetRoot, "config.yaml");
if (!existsSync(sourcePath) || !existsSync(targetPath)) return;
// Hermes owns this live file and normalizes it from the seed JSON into
// YAML. Parse both representations so a routine image refresh does not
// strand a healthy customer runtime before it can acknowledge its secrets.
const source = readProfileConfig(sourcePath);
const target = readProfileConfig(targetPath);
const sourceSkills = source?.skills;
let changed = false;
if (
sourceSkills &&
(typeof sourceSkills !== "object" || Array.isArray(sourceSkills))
)
throw new Error("live skills config rejected");
if (sourceSkills) {
const preserved = Object.fromEntries(
LIVE_SKILLS_CONFIG_KEYS.filter((key) =>
Object.prototype.hasOwnProperty.call(sourceSkills, key)
).map((key) => [key, structuredClone(sourceSkills[key])])
);
if (Object.keys(preserved).length > 0) {
target.skills = {
...(target.skills &&
typeof target.skills === "object" &&
!Array.isArray(target.skills)
? target.skills
: {}),
...preserved,
};
changed = true;
}
}
const sourcePlugins = source?.plugins;
if (
sourcePlugins &&
(typeof sourcePlugins !== "object" || Array.isArray(sourcePlugins))
) {
throw new Error("live plugins config rejected");
}
if (sourcePlugins) {
const preserved = Object.fromEntries(
LIVE_PLUGIN_CONFIG_KEYS.filter((key) =>
Object.prototype.hasOwnProperty.call(sourcePlugins, key)
).map((key) => [key, structuredClone(sourcePlugins[key])])
);
if (Object.keys(preserved).length > 0) {
target.plugins = {
...(target.plugins &&
typeof target.plugins === "object" &&
!Array.isArray(target.plugins)
? target.plugins
: {}),
...preserved,
};
changed = true;
}
}
const slackToolsets = preservedSlackToolsets(source, sourceRoot);
if (slackToolsets !== null) {
target.platform_toolsets = {
...(target.platform_toolsets &&
typeof target.platform_toolsets === "object" &&
!Array.isArray(target.platform_toolsets)
? target.platform_toolsets
: {}),
slack: slackToolsets,
};
changed = true;
}
if (!changed) return;
atomicWrite(
targetPath,
`${JSON.stringify(target, null, 2)}\n`,
lstatSync(targetPath).mode & 0o777
);
}
function mergeLiveMutableState(sourceRoot, targetRoot) {
if (!existsSync(sourceRoot)) return;
for (const name of LIVE_MUTABLE_STATE_ROOTS) {
const source = join(sourceRoot, name);
if (!existsSync(source)) continue;
const target = join(targetRoot, name);
mkdirSync(target, { recursive: true, mode: 0o700 });
mergeHermesState(source, target);
}
mergeLiveMutableConfig(sourceRoot, targetRoot);
}
function preservedLiveStateIdentity(
profileRoot,
{ normalizeLegacyNativeDefault = false } = {}
) {
if (!existsSync(profileRoot)) return null;
const entries = [];
const walk = (rootName, directory, relativePath = "") => {
for (const name of readdirSync(directory).sort()) {
const nextRelative = relativePath ? `${relativePath}/${name}` : name;
if (rootName === "skills" && nextRelative === "sellable") continue;
const pathValue = join(directory, name);
const link = lstatSync(pathValue);
if (link.isSymbolicLink())
throw new Error("live mutable state symlink rejected");
if (link.isDirectory()) walk(rootName, pathValue, nextRelative);
else if (link.isFile()) {
entries.push({
path: `${rootName}/${nextRelative}`,
sha256: sha256(readFileSync(pathValue)),
});
} else throw new Error("live mutable state entry rejected");
}
};
for (const rootName of LIVE_MUTABLE_STATE_ROOTS) {
const root = join(profileRoot, rootName);
if (existsSync(root)) walk(rootName, root);
}
const configPath = join(profileRoot, "config.yaml");
let mutableConfig = {};
let slackToolsets = null;
if (existsSync(configPath)) {
const config = readProfileConfig(configPath);
const skills = config?.skills;
if (skills && typeof skills === "object" && !Array.isArray(skills)) {
const liveSkills = Object.fromEntries(
LIVE_SKILLS_CONFIG_KEYS.filter((key) =>
Object.prototype.hasOwnProperty.call(skills, key)
).map((key) => [key, skills[key]])
);
if (Object.keys(liveSkills).length > 0) mutableConfig.skills = liveSkills;
}
const plugins = config?.plugins;
if (plugins && (typeof plugins !== "object" || Array.isArray(plugins))) {
throw new Error("live plugins config rejected");
}
if (plugins) {
const livePlugins = Object.fromEntries(
LIVE_PLUGIN_CONFIG_KEYS.filter((key) =>
Object.prototype.hasOwnProperty.call(plugins, key)
).map((key) => [key, plugins[key]])
);
if (Object.keys(livePlugins).length > 0)
mutableConfig.plugins = livePlugins;
}
slackToolsets = preservedSlackToolsets(config, profileRoot, {
normalizeLegacyNativeDefault,
});
}
return sha256(stableJson({ mutableConfig, entries, slackToolsets }));
}
function mcpFor(desired, profileId) {
return {
package: desired.mcpPackage,
workspaceLockId: desired.workspaceLockId,
requireWorkspaceLock: true,
tools: { include: [...desired.toolInclude] },
policyHash: desired.policyHash,
serviceCredential: {
reference: desired.serviceCredentialReference,
fingerprint: desired.serviceCredentialFingerprint,
generation: desired.serviceCredentialGeneration,
file: `/run/sellable-agent/${profileId}/service-credential`,
},
agentContext: {
workspaceId: desired.workspaceId,
agentId: desired.agentId,
generation: desired.serviceCredentialGeneration,
policyHash: desired.policyHash,
},
requestChannelPolicy: {
source: "_meta.sellableAgent.channelId",
selectedChannelIds: [...desired.slackChannelIds],
defaultChannelId: desired.slackDefaultChannelId,
memberAllowlist: [...desired.slackMemberAllowlist],
},
};
}
function renderedProfileFiles(desired) {
const profileId = deriveContainedProfileId(
desired.workspaceId,
desired.agentId,
desired.hostId
);
return {
"config.yaml": `${JSON.stringify(configFor(desired, profileId), null, 2)}\n`,
"sellable/mcp.json": `${JSON.stringify(mcpFor(desired, profileId), null, 2)}\n`,
"SOUL.md": desired.soul,
"skills/sellable/SKILL.md": desired.skill,
};
}
export function computeAgentNonSoulContentHash(fileHashes) {
const nonSoulFileHashes = Object.fromEntries(
Object.entries(fileHashes).filter(
([logicalPath]) => logicalPath !== "SOUL.md"
)
);
return sha256(stableJson(nonSoulFileHashes));
}
export function computeAgentProfileDigest(desired) {
const valid = validateDesired(desired);
if (!valid.ok) return valid;
const files = renderedProfileFiles(desired);
const fileHashes = Object.fromEntries(
Object.keys(files)
.sort()
.map((logicalPath) => [
logicalPath,
sha256(
logicalPath === "config.yaml"
? normalizedProfileConfigValueBytes(JSON.parse(files[logicalPath]))
: files[logicalPath]
),
])
);
return {
ok: true,
fileHashes,
profileDigest: sha256(stableJson({ ...manifestBody(desired), fileHashes })),
nonSoulContentHash: computeAgentNonSoulContentHash(fileHashes),
};
}
function atomicWrite(pathValue, bytes, mode) {
mkdirSync(dirname(pathValue), { recursive: true, mode: 0o700 });
const temp = join(
dirname(pathValue),
`.${basename(pathValue)}.${randomUUID()}.tmp`
);
writeFileSync(temp, bytes, { mode, flag: "wx" });
chmodSync(temp, mode);
const descriptor = openSync(temp, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
renameSync(temp, pathValue);
}
function fsyncDirectory(pathValue) {
const descriptor = openSync(pathValue, "r");
try {
fsyncSync(descriptor);
} finally {
closeSync(descriptor);
}
}
function renderStagedProfile(stageRoot, desired) {
mkdirSync(stageRoot, { recursive: true, mode: 0o700 });
const files = renderedProfileFiles(desired);
for (const [logicalPath, content] of Object.entries(files)) {
atomicWrite(
join(stageRoot, logicalPath),
content,
logicalPath.endsWith(".md") ? 0o444 : 0o600
);
}
const fileHashes = Object.fromEntries(
Object.keys(files)
.sort()
.map((logicalPath) => [
logicalPath,
sha256(
logicalPath === "config.yaml"
? normalizedProfileConfigBytes(join(stageRoot, logicalPath))
: readFileSync(join(stageRoot, logicalPath))
),
])
);
const base = manifestBody(desired);
const profileDigest = sha256(stableJson({ ...base, fileHashes }));
const manifest = { ...base, fileHashes, profileDigest };
atomicWrite(
join(stageRoot, "agent-profile.json"),
`${JSON.stringify(manifest, null, 2)}\n`,
0o600
);
fsyncDirectory(stageRoot);
return manifest;
}
function inventory(root, { skipTransient = false } = {}) {
const files = [];
const walk = (pathValue, relativePath) => {
const link = lstatSync(pathValue);
if (link.isSymbolicLink()) throw new Error("profile symlink rejected");
if (link.isDirectory()) {
for (const name of readdirSync(pathValue).sort()) {
const nextRelative = relativePath
? `${relativePath}/${name}`
: name;
if (skipTransient && isHermesTransientProfilePath(nextRelative))
continue;
walk(
join(pathValue, name),
nextRelative
);
}
return;
}
if (!link.isFile()) throw new Error("profile entry rejected");
files.push(relativePath);
};
walk(root, "");
return files.sort();
}
function treeInventory(root) {
const entries = [];
const walk = (pathValue, relativePath) => {
const link = lstatSync(pathValue);
if (link.isSymbolicLink()) throw new Error("profile symlink rejected");
entries.push({
path: relativePath || ".",
type: link.isDirectory() ? "directory" : link.isFile() ? "file" : "other",
uid: link.uid,
gid: link.gid,
mode: link.mode & 0o777,
});
if (link.isDirectory()) {
for (const name of readdirSync(pathValue).sort()) {
walk(
join(pathValue, name),
relativePath ? `${relativePath}/${name}` : name
);
}
}
};
walk(root, "");
return entries;
}
export function createPosixProfileOwnershipPort() {
return {
async apply({ profileRoot, uid, gid }) {
const entries = treeInventory(profileRoot).sort(
(a, b) => b.path.length - a.path.length
);
for (const entry of entries) {
const pathValue =
entry.path === "." ? profileRoot : join(profileRoot, entry.path);
chownSync(pathValue, uid, gid);
chmodSync(pathValue, entry.type === "directory" ? 0o500 : 0o400);
}
return { ok: true };
},
async observe({ profileRoot }) {
return { ok: true, entries: treeInventory(profileRoot) };
},
async remove({ profileRoot }) {
if (existsSync(profileRoot))
rmSync(profileRoot, { recursive: true, force: true });
return { ok: true };
},
};
}
function validateRuntimeOwnership(observed, runtimeIdentity) {
if (!observed?.ok || !Array.isArray(observed.entries)) return false;
const expectedPaths = [
".",
"agent-profile.json",
"config.yaml",
"sellable",
"sellable/mcp.json",
"SOUL.md",
"skills",
"skills/sellable",
"skills/sellable/SKILL.md",
].sort();
const observedPaths = observed.entries.map((entry) => entry.path);
if (expectedPaths.some((pathValue) => !observedPaths.includes(pathValue))) {
return false;
}
return observed.entries.every(
(entry) =>
entry.uid === runtimeIdentity.uid &&
entry.gid === runtimeIdentity.gid &&
(entry.type === "directory"
? entry.mode === 0o500
: entry.type === "file" && entry.mode === 0o400)
);
}
export function validateAgentProfileBundle({ profileRoot, desired }) {
const valid = validateDesired(desired);
if (!valid.ok) return valid;
try {
if (
!isAbsolute(profileRoot) ||
!existsSync(profileRoot) ||
lstatSync(profileRoot).isSymbolicLink()
) {
return { ok: false, code: "profile_path_invalid" };
}
const foundInventory = inventory(profileRoot);
const serverOwned = [...AGENT_PROFILE_INVENTORY].sort();
if (
serverOwned.some(
(logicalPath) => !foundInventory.includes(logicalPath)
) ||
foundInventory.some(
(logicalPath) =>
!serverOwned.includes(logicalPath) &&
!["skills/", "plugins/", "plugin-data/"].some((prefix) =>
logicalPath.startsWith(prefix)
)
)
) {
return { ok: false, code: "profile_inventory_open" };
}
const manifestPath = join(profileRoot, "agent-profile.json");
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
if (!exactKeys(manifest, MANIFEST_KEYS))
return { ok: false, code: "manifest_schema_invalid" };
const expectedBody = manifestBody(desired);
if (
Object.entries(expectedBody).some(
([key, value]) => stableJson(manifest[key]) !== stableJson(value)
)
) {
return { ok: false, code: "manifest_identity_drift" };
}
const expectedHashes = Object.fromEntries(
AGENT_PROFILE_INVENTORY.filter((path) => path !== "agent-profile.json")
.sort()
.map((logicalPath) => [
logicalPath,
sha256(
logicalPath === "config.yaml"
? normalizedProfileConfigBytes(join(profileRoot, logicalPath))
: readFileSync(join(profileRoot, logicalPath))
),
])
);
if (stableJson(manifest.fileHashes) !== stableJson(expectedHashes)) {
return { ok: false, code: "profile_file_hash_drift" };
}
if (
manifest.profileDigest !==
sha256(stableJson({ ...expectedBody, fileHashes: expectedHashes }))
) {
return { ok: false, code: "profile_digest_drift" };
}
const config = JSON.parse(
readFileSync(join(profileRoot, "config.yaml"), "utf8")
);
const mcp = JSON.parse(
readFileSync(join(profileRoot, "sellable", "mcp.json"), "utf8")
);
if (
stableJson(profileConfigWithoutLiveState(config)) !==
stableJson(
profileConfigWithoutLiveState(configFor(desired, manifest.profileId))
)
) {
return { ok: false, code: "config_drift" };
}
if (stableJson(mcp) !== stableJson(mcpFor(desired, manifest.profileId))) {
return { ok: false, code: "mcp_policy_drift" };
}
if (
readFileSync(join(profileRoot, "SOUL.md"), "utf8") !== desired.soul ||
readFileSync(
join(profileRoot, "skills", "sellable", "SKILL.md"),
"utf8"
) !== desired.skill
)
return { ok: false, code: "content_drift" };
for (const logicalPath of foundInventory) {
const pathValue = join(profileRoot, logicalPath);
const link = lstatSync(pathValue);
if (
(link.mode & 0o022) !== 0 ||
RAW_SECRET.test(readFileSync(pathValue, "utf8"))
) {
return { ok: false, code: "secret_or_mode_drift" };
}
}
return {
ok: true,
manifest,
hashes: {
...expectedHashes,
profile: manifest.profileDigest,
nonSoulContent: computeAgentNonSoulContentHash(expectedHashes),
},
};
} catch {
return { ok: false, code: "profile_validation_failed" };
}
}
function secretBinding(desired, servicePurpose) {
return {
workspaceId: desired.workspaceId,
agentId: desired.agentId,
installationId: desired.installationId,
generation: desired.slackGeneration,
hostId: desired.hostId,
workerId: desired.workerId,
fence: desired.fence,
servicePurpose,
};
}
function sameBinding(value, expected) {
return (
exactKeys(value, Object.keys(expected)) &&
Object.entries(expected).every(
([key, expectedValue]) => value[key] === expectedValue
)
);
}
function removeDirect(root, pathValue) {
const realRoot = realpathSync(root);
const resolved = resolve(pathValue);
if (dirname(resolved) !== realRoot || !isInside(realRoot, resolved))
throw new Error("path confinement failed");
if (existsSync(resolved)) {
if (lstatSync(resolved).isSymbolicLink())
throw new Error("symlink removal rejected");
rmSync(resolved, { recursive: true, force: true });
}
}
function cleanupSecrets(runtimeSecretsRoot, profileId) {
try {
const root = realpathSync(runtimeSecretsRoot);
const target = join(root, profileId);
if (
dirname(target) === root &&
existsSync(target) &&
!lstatSync(target).isSymbolicLink()
) {
rmSync(target, { recursive: true, force: true });
}
} catch {}
}
async function materializeRuntimeSecrets({
desired,
runtimeSecretsRoot,
ports,
serviceCredential,
runtimeIdentity,
}) {
const profileId = deriveContainedProfileId(
desired.workspaceId,
desired.agentId,
desired.hostId
);
try {
const appBinding = secretBinding(desired, "slack-app");
const xapp = await ports.hostAppSecret.resolveHostSecret({
...appBinding,
reference: desired.xappReference,
expectedFingerprint: desired.xappFingerprint,
});
if (
!sameBinding(xapp?.binding, appBinding) ||
typeof xapp?.secret !== "string" ||
!xapp.secret ||
xapp.fingerprint !== desired.xappFingerprint ||
sha256(xapp.secret).slice(0, 16) !== desired.xappFingerprint
)
return { ok: false, code: "secret_binding_rejected" };
const botBinding = secretBinding(desired, "slack-bot");
const xoxb = await ports.workspaceBotSecret.decryptWorkspaceSecret({
...botBinding,
reference: desired.xoxbReference,
ciphertextFingerprint: desired.xoxbCiphertextFingerprint,
});
if (
!sameBinding(xoxb?.binding, botBinding) ||
typeof xoxb?.secret !== "string" ||
!xoxb.secret ||
xoxb.generation !== desired.slackGeneration ||
xoxb.fingerprint !== desired.xoxbCiphertextFingerprint ||
xoxb.fingerprint !== sha256(xoxb.secret).slice(0, 16)
)
return { ok: false, code: "secret_binding_rejected" };
const credentialBinding = {
workspaceId: desired.workspaceId,
agentId: desired.agentId,
installationId: desired.installationId,
generation: desired.serviceCredentialGeneration,
hostId: desired.hostId,
workerId: desired.workerId,
fence: desired.fence,
servicePurpose: "mcp-service",
};
if (
!sameBinding(serviceCredential?.binding, credentialBinding) ||
typeof serviceCredential?.secret !== "string" ||
!serviceCredential.secret.startsWith("sat_") ||
serviceCredential.generation !== desired.serviceCredentialGeneration ||
serviceCredential.fingerprint !== desired.serviceCredentialFingerprint ||
sha256(serviceCredential.secret).slice(0, 16) !==
desired.serviceCredentialFingerprint
)
return { ok: false, code: "secret_binding_rejected" };
const credential = await ports.credential.install({
desired,
profileId,
runtimeIdentity,
serviceCredential,
});
if (
!credential?.ok ||
credential.profileId !== profileId ||
credential.fingerprint !== desired.serviceCredentialFingerprint ||
credential.generation !== desired.serviceCredentialGeneration ||
typeof credential.path !== "string" ||
!isAbsolute(credential.path)
)
return { ok: false, code: "service_credential_install_rejected" };
const root = realpathSync(runtimeSecretsRoot);
const serviceRoot = join(root, profileId);
if (dirname(serviceRoot) !== root)
return { ok: false, code: "secret_path_rejected" };
cleanupSecrets(root, profileId);
mkdirSync(serviceRoot, { recursive: false, mode: 0o700 });
const appPath = join(serviceRoot, "slack-app");
const botPath = join(serviceRoot, "slack-bot");
atomicWrite(appPath, `${xapp.secret}\n`, 0o600);
atomicWrite(botPath, `${xoxb.secret}\n`, 0o600);
fsyncDirectory(serviceRoot);
const workerUid = process.getuid?.() ?? lstatSync(serviceRoot).uid;
const workerGid = process.getgid?.() ?? lstatSync(serviceRoot).gid;
if (
lstatSync(serviceRoot).uid !== workerUid ||
lstatSync(serviceRoot).gid !== workerGid ||
(lstatSync(serviceRoot).mode & 0o777) !== 0o700 ||
[appPath, botPath].some((pathValue) => {
const link = lstatSync(pathValue);
return (
link.isSymbolicLink() ||
!link.isFile() ||
link.uid !== workerUid ||
link.gid !== workerGid ||
(link.mode & 0o777) !== 0o600
);
})
)
throw new Error("worker secret ownership mismatch");
return {
ok: true,
files: [appPath, botPath, credential.path],
xappFingerprint: xapp.fingerprint,
xoxbFingerprint: xoxb.fingerprint,
serviceCredentialFingerprint: serviceCredential.fingerprint,
serviceCredentialGeneration: serviceCredential.generation,
};
} catch {
cleanupSecrets(runtimeSecretsRoot, profileId);
try {
await ports.credential.cleanup({ profileId });
} catch {}
return { ok: false, code: "secret_materialization_failed" };
}
}
function reuseInstalledRuntimeSecrets({
desired,
runtimeSecretsRoot,
mcpCredentialRoot,
observation,
runtimeIdentity,
}) {
const profileId = deriveContainedProfileId(
desired.workspaceId,
desired.agentId,
desired.hostId
);
try {
if (
observation?.profileId !== profileId ||
observation.workspaceId !== desired.workspaceId ||
observation.agentId !== desired.agentId ||
observation.slack?.xappPresent !== true ||
observation.slack?.xoxbPresent !== true ||
observation.serviceCredential?.present !== true ||
observation.slack.xappFingerprint !== desired.xappFingerprint ||
observation.slack.xoxbFingerprint !== desired.xoxbCiphertextFingerprint ||
observation.slack.generation !== desired.slackGeneration ||
observation.serviceCredential.fingerprint !==
desired.serviceCredentialFingerprint ||
observation.serviceCredential.generation !==
desired.serviceCredentialGeneration
)
return { ok: false, code: "installed_secret_identity_rejected" };
const slackRoot = privateRoot(runtimeSecretsRoot);
const mcpRoot = resolve(mcpCredentialRoot ?? "");
if (!slackRoot || !isAbsolute(mcpRoot))
return { ok: false, code: "installed_secret_root_rejected" };
const slackProfileRoot = join(slackRoot, profileId);
const mcpProfileRoot = join(mcpRoot, profileId);
const files = [
join(slackProfileRoot, "slack-app"),
join(slackProfileRoot, "slack-bot"),
join(mcpProfileRoot, "service-credential"),
];
for (const [index, pathValue] of files.slice(0, 2).entries()) {
const link = lstatSync(pathValue);
const expectedUid = process.getuid?.() ?? link.uid;
const expectedGid = process.getgid?.() ?? link.gid;
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o777) !== 0o600 ||
link.uid !== expectedUid ||
link.gid !== expectedGid
)
throw new Error("installed secret file rejected");
}
return {
ok: true,
reused: true,
files,
xappFingerprint: observation.slack.xappFingerprint,
xoxbFingerprint: observation.slack.xoxbFingerprint,
serviceCredentialFingerprint: observation.serviceCredential.fingerprint,
serviceCredentialGeneration: observation.serviceCredential.generation,
};
} catch {
return { ok: false, code: "installed_secret_readback_rejected" };
}
}
function portsValid(ports) {
return Object.entries(PROFILE_MATERIALIZER_PORTS).every(
([group, methods]) =>
ports?.[group] &&
methods.every((method) => typeof ports[group][method] === "function")
);
}
function expectedStaticObservation({
desired,
profileId,
validation,
secrets,
}) {
return {
profileId,
workspaceId: desired.workspaceId,
agentId: desired.agentId,
revisionId: desired.revisionId,
hashes: {
package: sha256(
`${desired.installerPackage}\0${desired.mcpPackage}\0${desired.hermesVersion}\0${HERMES_AGENT_BRIDGE_CONTRACT.contractDigest}`
),
config: validation.hashes["config.yaml"],
mcp: validation.hashes["sellable/mcp.json"],
content: validation.hashes.profile,
nonSoulContent: validation.hashes.nonSoulContent,
policy: desired.policyHash,
slackManifest: desired.slackManifestHash,
soul: desired.soulHash,
skills: desired.skillsHash,
provider: desired.providerFingerprint,
},
slack: {
app: desired.slackAppHash,
team: desired.slackTeamHash,
bot: desired.slackBotHash,
channels: desired.slackChannelHash,
defaultChannel: desired.slackDefaultChannelHash,
memberAllowlist: desired.slackMemberAllowlistHash,
channelIds: [...desired.slackChannelIds],
defaultChannelId: desired.slackDefaultChannelId,
memberIds: [...desired.slackMemberAllowlist],
generation: desired.slackGeneration,
xappPresent: true,
xoxbPresent: true,
xappFingerprint: secrets.xappFingerprint,
xoxbFingerprint: secrets.xoxbFingerprint,
},
serviceCredential: {
present: true,
fingerprint: secrets.serviceCredentialFingerprint,
generation: secrets.serviceCredentialGeneration,
},
hermesBridge: {
bridgeId: HERMES_AGENT_BRIDGE_CONTRACT.bridgeId,
contractDigest: HERMES_AGENT_BRIDGE_CONTRACT.contractDigest,
hermesVersion: HERMES_AGENT_BRIDGE_CONTRACT.hermesVersion,
state: "installed",
reasons: [],
files: HERMES_AGENT_BRIDGE_CONTRACT.files.map((file) => ({
path: file.path,
sha256: file.patchedSha256,
})),
status: "verified",
},
};
}
function validateIndependentObservation(observed, expected, runtimeIdentity) {
if (!observed || !exactKeys(observed, [...Object.keys(expected), "process"]))
return false;
if (
!exactKeys(observed.process, [
"ready",
"pid",
"startTicks",
"bootId",
"serviceUser",
"uid",
])
) {
return false;
}
const { process, ...staticObserved } = observed;
const observedPackageHash = staticObserved.hashes?.package;
const expectedWithoutPackage = {
...expected,
hashes: { ...expected.hashes, package: undefined },
};
const observedWithoutPackage = {
...staticObserved,
hashes: { ...staticObserved.hashes, package: undefined },
};
return (
SHA256.test(observedPackageHash ?? "") &&
stableJson(observedWithoutPackage) === stableJson(expectedWithoutPackage) &&
process.ready === true &&
Number.isSafeInteger(process.pid) &&
process.pid > 1 &&
/^[1-9][0-9]{0,31}$/.test(process.startTicks ?? "") &&
typeof process.bootId === "string" &&
/^[A-Za-z0-9_-]{1,128}$/.test(process.bootId) &&
process.serviceUser === runtimeIdentity.user &&
process.uid === runtimeIdentity.uid
);
}
const PROMOTION_TRANSACTION_VERSION = "sellable-agent-profile-promotion/v1";
const PROMOTION_SIGNATURE = /^[A-Za-z0-9_-]{64,512}$/;
function promotionTransactionPath(profilesRoot, profileId) {
return join(profilesRoot, `.${profileId}.promotion.json`);
}
function promotionJournal(input) {
const journal = input.promotionJournal ?? input.ports?.promotionJournal;
const operationId = input.operationId ?? journal?.operationId;
const effectId = input.effectId ?? journal?.effectId;
if (
!journal ||
typeof journal.sign !== "function" ||
typeof journal.verify !== "function" ||
!Number.isSafeInteger(journal.signerGeneration) ||
journal.signerGeneration < 1 ||
!SAFE_ID.test(operationId ?? "") ||
!/^sahe_[a-f0-9]{64}$/.test(effectId ?? "")
)
throw new Error("promotion journal authority rejected");
return { journal, operationId, effectId };
}
function promotionSignedBody(transaction) {
const { signature: _signature, ...body } = transaction;
return body;
}
function signPromotionTransaction(transaction, authority) {
const signature = authority.journal.sign(promotionSignedBody(transaction));
if (!PROMOTION_SIGNATURE.test(signature ?? ""))
throw new Error("promotion signature rejected");
return { ...transaction, signature };
}
function promotionObservationCore(observation) {
const {
profileDigest: _profileDigest,
tools: _tools,
revisionSequence: _revisionSequence,
...core
} = observation ?? {};
return core;
}
function targetObservationDigest(observation, transaction) {
const core = promotionObservationCore(observation);
if (
core.profileId !== transaction.profileId ||
core.revisionId !== transaction.targetRevisionId ||
core.hashes?.content !== transaction.targetProfileDigest ||
!SHA256.test(core.hashes?.package ?? "") ||
!core.process?.ready ||
!Number.isSafeInteger(core.process?.pid) ||
core.process.pid < 2 ||
!/^[1-9][0-9]{0,31}$/.test(core.process?.startTicks ?? "")
)
throw new Error("promotion target observation rejected");
return sha256(stableJson(core));
}
function priorObservationCore(observation) {
const {
process: _process,
revisionSequence: _revisionSequence,
tools: _tools,
...core
} = observation ?? {};
return core;
}
function validPriorObservation(observation, profileId) {
return Boolean(
observation &&
observation.profileId === profileId &&
SAFE_ID.test(observation.workspaceId ?? "") &&
SAFE_ID.test(observation.agentId ?? "") &&
SAFE_ID.test(observation.revisionId ?? "") &&
SHA256.test(observation.hashes?.content ?? "") &&
FINGERPRINT.test(observation.slack?.xappFingerprint ?? "") &&
FINGERPRINT.test(observation.slack?.xoxbFingerprint ?? "") &&
FINGERPRINT.test(observation.serviceCredential?.fingerprint ?? "") &&
Number.isSafeInteger(observation.serviceCredential?.generation) &&
observation.serviceCredential.generation > 0 &&
!RAW_SECRET.test(JSON.stringify(observation))
);
}
function writePromotionTransaction({
profilesRoot,
profileId,
desired,
targetProfileDigest,
reusedSecrets,
priorObservation,
authority,
}) {
const transaction = signPromotionTransaction(
{
version: PROMOTION_TRANSACTION_VERSION,
status: "PREPARED",
profileId,
operationId: authority.operationId,
effectId: authority.effectId,
signerGeneration: authority.journal.signerGeneration,
targetRevisionId: desired.revisionId,
targetRevisionSequence: desired.revisionSequence,
targetProfileDigest,
fence: desired.fence,
reusedSecrets,
priorObservation: reusedSecrets ? priorObservation : null,
targetObservationDigest: null,
},
authority
);
if (reusedSecrets && !validPriorObservation(priorObservation, profileId)) {
throw new Error("prior-good promotion evidence rejected");
}
atomicWrite(
promotionTransactionPath(profilesRoot, profileId),
`${JSON.stringify(transaction, null, 2)}\n`,
0o600
);
fsyncDirectory(profilesRoot);
}
function removePromotionTransaction(profilesRoot, profileId) {
const pathValue = promotionTransactionPath(profilesRoot, profileId);
if (existsSync(pathValue)) {
const link = lstatSync(pathValue);
if (link.isSymbolicLink() || !link.isFile())
throw new Error("promotion transaction rejected");
rmSync(pathValue);
fsyncDirectory(profilesRoot);
}
}
function readPromotionTransaction(
profilesRoot,
profileId,
authority,
currentFence
) {
const pathValue = promotionTransactionPath(profilesRoot, profileId);
if (!existsSync(pathValue)) return null;
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o777) !== 0o600
) {
throw new Error("promotion transaction rejected");
}
const transaction = JSON.parse(readFileSync(pathValue, "utf8"));
if (
!exactKeys(transaction, [
"version",
"status",
"profileId",
"operationId",
"effectId",
"signerGeneration",
"targetRevisionId",
"targetRevisionSequence",
"targetProfileDigest",
"fence",
"reusedSecrets",
"priorObservation",
"targetObservationDigest",
"signature",
]) ||
transaction.version !== PROMOTION_TRANSACTION_VERSION ||
!["PREPARED", "COMMITTED"].includes(transaction.status) ||
transaction.profileId !== profileId ||
!SAFE_ID.test(transaction.targetRevisionId ?? "") ||
transaction.operationId !== authority.operationId ||
transaction.effectId !== authority.effectId ||
transaction.signerGeneration !== authority.journal.signerGeneration ||
!Number.isSafeInteger(transaction.targetRevisionSequence) ||
transaction.targetRevisionSequence < 1 ||
!SHA256.test(transaction.targetProfileDigest ?? "") ||
!/^[1-9][0-9]{0,19}$/.test(transaction.fence ?? "") ||
typeof transaction.reusedSecrets !== "boolean" ||
(transaction.reusedSecrets &&
!validPriorObservation(transaction.priorObservation, profileId)) ||
(!transaction.reusedSecrets && transaction.priorObservation !== null) ||
(transaction.status === "PREPARED" &&
transaction.targetObservationDigest !== null) ||
(transaction.status === "COMMITTED" &&
!SHA256.test(transaction.targetObservationDigest ?? "")) ||
!PROMOTION_SIGNATURE.test(transaction.signature ?? "") ||
BigInt(currentFence) < BigInt(transaction.fence) ||
authority.journal.verify(
promotionSignedBody(transaction),
transaction.signature
) !== true
)
throw new Error("promotion transaction schema rejected");
return transaction;
}
async function restartAndProvePrior({
profileRoot,
profileId,
priorObservation,
ports,
runtimeIdentity,
}) {
try {
const manifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
if (
manifest.profileId !== profileId ||
manifest.revisionId !== priorObservation.revisionId ||
manifest.profileDigest !== priorObservation.hashes.content
)
return false;
await ports.gateway.start({
profileId,
profileRoot,
identity: runtimeIdentity,
argv: ["hermes", "--profile", profileId, "gateway", "run"],
env: { HOME: "/profile", NODE_ENV: "production" },
slackDelivery: {
supervisorRunScript: "sellable-agent-runtime/run",
appFd: 8,
botFd: 9,
},
evidence: {
revisionId: manifest.revisionId,
profileDigest: manifest.profileDigest,
xappFingerprint: priorObservation.slack.xappFingerprint,
xoxbFingerprint: priorObservation.slack.xoxbFingerprint,
serviceCredentialFingerprint:
priorObservation.serviceCredential.fingerprint,
serviceCredentialGeneration:
priorObservation.serviceCredential.generation,
},
});
const restored = await ports.gateway.observe({
profileId,
profileRoot,
rollback: true,
});
const process = restored?.process;
return Boolean(
stableJson(priorObservationCore(restored)) ===
stableJson(priorObservationCore(priorObservation)) &&
process?.ready &&
Number.isSafeInteger(process.pid) &&
process.pid > 1 &&
/^[1-9][0-9]{0,31}$/.test(process.startTicks ?? "") &&
process.serviceUser === runtimeIdentity.user &&
process.uid === runtimeIdentity.uid
);
} catch {
return false;
}
}
export async function recoverInterruptedProfilePromotion(input = {}) {
const valid = validateDesired(input.desired);
const profilesRoot = traversableProfilesRoot(input.profilesRoot);
const runtimeSecretsRoot = privateRoot(input.runtimeSecretsRoot);
if (
!valid.ok ||
!profilesRoot ||
!runtimeSecretsRoot ||
!portsValid(input.ports)
) {
return { ok: false, status: "RECOVERY_INPUT_REJECTED" };
}
const profileId = deriveContainedProfileId(
input.desired.workspaceId,
input.desired.agentId,
input.desired.hostId
);
try {
const authority = promotionJournal(input);
const transaction = readPromotionTransaction(
profilesRoot,
profileId,
authority,
input.desired.fence
);
if (!transaction) return { ok: true, status: "NONE", profileId };
const profileRoot = join(profilesRoot, profileId);
const backupRoot = join(profilesRoot, `${profileId}.rollback-prior-good`);
if (transaction.status === "COMMITTED") {
if (!existsSync(profileRoot))
throw new Error("committed promotion target absent");
const activeManifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
if (
activeManifest.profileId !== profileId ||
activeManifest.revisionId !== transaction.targetRevisionId ||
activeManifest.revisionSequence !==
transaction.targetRevisionSequence ||
activeManifest.profileDigest !== transaction.targetProfileDigest
)
throw new Error("committed promotion target rejected");
const committedObservation = await input.ports.gateway.observe({
profileId,
profileRoot,
committedRecovery: true,
});
if (
targetObservationDigest(committedObservation, transaction) !==
transaction.targetObservationDigest
) {
throw new Error("committed promotion observation rejected");
}
if (existsSync(backupRoot)) {
await input.ports.ownership.remove({
profileId,
profileRoot: backupRoot,
slot: "BACKUP",
revisionId: transaction.targetRevisionId,
fence: transaction.fence,
});
}
if (existsSync(backupRoot))
throw new Error("committed promotion backup cleanup unproven");
removePromotionTransaction(profilesRoot, profileId);
return { ok: true, status: "COMMITTED_FINALIZED", profileId };
}
if (existsSync(backupRoot)) {
if (existsSync(profileRoot)) {
const activeManifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
if (
activeManifest.profileId !== profileId ||
activeManifest.revisionId !== transaction.targetRevisionId ||
activeManifest.revisionSequence !==
transaction.targetRevisionSequence ||
activeManifest.profileDigest !== transaction.targetProfileDigest
)
throw new Error("promotion transaction active target rejected");
await input.ports.ownership.remove({
profileId,
profileRoot,
slot: "ACTIVE",
revisionId: transaction.targetRevisionId,
fence: transaction.fence,
});
}
renameSync(backupRoot, profileRoot);
fsyncDirectory(profilesRoot);
}
if (!existsSync(profileRoot)) throw new Error("prior-good profile absent");
if (!transaction.reusedSecrets) {
cleanupSecrets(runtimeSecretsRoot, profileId);
await input.ports.credential.cleanup({ profileId });
removePromotionTransaction(profilesRoot, profileId);
return { ok: false, status: "RECOVERY_UNPROVEN", profileId };
}
const proven = await restartAndProvePrior({
profileRoot,
profileId,
priorObservation: transaction.priorObservation,
ports: input.ports,
runtimeIdentity: input.runtimeIdentity,
});
if (!proven) return { ok: false, status: "RECOVERY_UNPROVEN", profileId };
removePromotionTransaction(profilesRoot, profileId);
return {
ok: true,
status: "RECOVERED_AND_RESTARTED",
profileId,
priorObservation: transaction.priorObservation,
};
} catch {
return { ok: false, status: "RECOVERY_UNPROVEN", profileId };
}
}
export function commitRetainedProfilePromotion(input = {}) {
const valid = validateDesired(input.desired);
const profilesRoot = traversableProfilesRoot(input.profilesRoot);
if (!valid.ok || !profilesRoot)
return { ok: false, status: "COMMIT_INPUT_REJECTED" };
const profileId = deriveContainedProfileId(
input.desired.workspaceId,
input.desired.agentId,
input.desired.hostId
);
try {
const authority = promotionJournal(input);
const transaction = readPromotionTransaction(
profilesRoot,
profileId,
authority,
input.desired.fence
);
const profileRoot = join(profilesRoot, profileId);
const backupRoot = join(profilesRoot, `${profileId}.rollback-prior-good`);
if (!existsSync(profileRoot))
throw new Error("promotion commit target absent");
const activeManifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
if (
!transaction ||
transaction.targetRevisionId !== input.desired.revisionId ||
transaction.targetRevisionSequence !== input.desired.revisionSequence ||
transaction.fence !== input.desired.fence ||
transaction.targetProfileDigest !== activeManifest.profileDigest ||
activeManifest.profileId !== profileId ||
activeManifest.revisionId !== transaction.targetRevisionId ||
activeManifest.revisionSequence !== transaction.targetRevisionSequence ||
!existsSync(backupRoot)
)
throw new Error("promotion commit evidence rejected");
if (transaction.status === "PREPARED") {
const observedDigest = targetObservationDigest(
input.targetObservation,
transaction
);
atomicWrite(
promotionTransactionPath(profilesRoot, profileId),
`${JSON.stringify(
signPromotionTransaction(
{
...promotionSignedBody(transaction),
status: "COMMITTED",
targetObservationDigest: observedDigest,
},
authority
),
null,
2
)}\n`,
0o600
);
fsyncDirectory(profilesRoot);
} else if (transaction.status !== "COMMITTED") {
throw new Error("promotion commit state rejected");
}
return { ok: true, status: "COMMITTED_PENDING_CLEANUP", profileId };
} catch {
return { ok: false, status: "COMMIT_UNPROVEN", profileId };
}
}
export function finalizeRetainedProfilePromotion(input = {}) {
const valid = validateDesired(input.desired);
const profilesRoot = traversableProfilesRoot(input.profilesRoot);
if (!valid.ok || !profilesRoot)
return { ok: false, status: "FINALIZE_INPUT_REJECTED" };
const profileId = deriveContainedProfileId(
input.desired.workspaceId,
input.desired.agentId,
input.desired.hostId
);
try {
const authority = promotionJournal(input);
const transaction = readPromotionTransaction(
profilesRoot,
profileId,
authority,
input.desired.fence
);
const profileRoot = join(profilesRoot, profileId);
if (
!transaction ||
transaction.status !== "COMMITTED" ||
transaction.targetRevisionId !== input.desired.revisionId ||
transaction.targetRevisionSequence !== input.desired.revisionSequence ||
transaction.fence !== input.desired.fence ||
existsSync(join(profilesRoot, `${profileId}.rollback-prior-good`)) ||
!existsSync(profileRoot)
)
throw new Error("promotion finalize evidence rejected");
const activeManifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
if (
activeManifest.profileId !== profileId ||
activeManifest.revisionId !== transaction.targetRevisionId ||
activeManifest.revisionSequence !== transaction.targetRevisionSequence ||
activeManifest.profileDigest !== transaction.targetProfileDigest
)
throw new Error("promotion finalize target rejected");
removePromotionTransaction(profilesRoot, profileId);
return { ok: true, status: "FINALIZED", profileId };
} catch {
return { ok: false, status: "FINALIZE_UNPROVEN", profileId };
}
}
async function rollback({
profilesRoot,
profileRoot,
backupRoot,
runtimeSecretsRoot,
profileId,
desired,
ports,
started,
preserveSecrets,
priorObservation,
runtimeIdentity,
}) {
if (started) {
try {
await ports.gateway.stop({
profileId,
reason: "validation_or_readback_failed",
});
} catch {}
}
if (existsSync(profileRoot))
await ports.ownership.remove({
profileId,
profileRoot,
slot: "ACTIVE",
revisionId: desired.revisionId,
fence: desired.fence,
});
let outcome = "REMOVED";
if (existsSync(backupRoot)) {
renameSync(backupRoot, profileRoot);
outcome = "RESTORED";
}
if (!preserveSecrets) {
cleanupSecrets(runtimeSecretsRoot, profileId);
try {
await ports.credential.cleanup({ profileId });
} catch {}
}
fsyncDirectory(profilesRoot);
if (outcome === "RESTORED") {
if (!preserveSecrets || !priorObservation) return "RESTORE_UNPROVEN";
return (await restartAndProvePrior({
profileRoot,
profileId,
priorObservation,
ports,
runtimeIdentity,
}))
? "RESTORED_AND_RESTARTED"
: "RESTORE_UNPROVEN";
}
return outcome;
}
async function materializeAgentProfileLocked(input = {}) {
const valid = validateDesired(input.desired);
if (!valid.ok)
return { ok: false, status: "DESIRED_REJECTED", code: valid.code };
if (!portsValid(input.ports)) return { ok: false, status: "PORT_REJECTED" };
const profilesRoot = traversableProfilesRoot(input.profilesRoot);
const runtimeSecretsRoot = privateRoot(input.runtimeSecretsRoot);
if (!profilesRoot || !runtimeSecretsRoot)
return { ok: false, status: "PATH_REJECTED" };
if (
!input.runtimeIdentity ||
input.runtimeIdentity.user !== "sellable-agent-runtime" ||
!Number.isSafeInteger(input.runtimeIdentity.uid) ||
input.runtimeIdentity.uid < 1 ||
!Number.isSafeInteger(input.runtimeIdentity.gid) ||
input.runtimeIdentity.gid < 1
)
return { ok: false, status: "IDENTITY_REJECTED" };
const desired = input.desired;
const profileId = deriveContainedProfileId(
desired.workspaceId,
desired.agentId,
desired.hostId
);
const profileRoot = join(profilesRoot, profileId);
const stagingRoot = join(
profilesRoot,
`${profileId}.staging-${desired.revisionId}-${desired.fence}`
);
const backupRoot = join(profilesRoot, `${profileId}.rollback-prior-good`);
let startAttempted = false;
let promoted = false;
let recoveredInterruptedPromotion = false;
let priorObservation = null;
let reusedSecrets = false;
try {
const authority = promotionJournal(input);
const recovery = await recoverInterruptedProfilePromotion(input);
if (!recovery.ok)
return {
ok: false,
status: recovery.status,
profileId,
recoveryPredicate: "exact_prior_good_restart_and_observation_required",
};
recoveredInterruptedPromotion =
recovery.status === "RECOVERED_AND_RESTARTED";
if (existsSync(backupRoot)) {
return {
ok: false,
status: "RECOVERY_UNPROVEN",
profileId,
recoveryPredicate: "promotion_transaction_evidence_required",
};
}
await input.ports.ownership.remove({
profileId,
profileRoot: stagingRoot,
slot: "STAGING",
revisionId: desired.revisionId,
fence: desired.fence,
});
const liveStateIdentity = preservedLiveStateIdentity(profileRoot);
renderStagedProfile(stagingRoot, desired);
mergeLiveMutableState(profileRoot, stagingRoot);
if (
liveStateIdentity !== null &&
preservedLiveStateIdentity(stagingRoot) !== liveStateIdentity
) {
throw new Error("live mutable state identity changed during staging");
}
const staged = validateAgentProfileBundle({
profileRoot: stagingRoot,
desired,
});
if (!staged.ok) {
removeDirect(profilesRoot, stagingRoot);
return { ok: false, status: "VALIDATION_REJECTED", code: staged.code };
}
const ownershipApplied = await input.ports.ownership.apply({
profileRoot: stagingRoot,
uid: input.runtimeIdentity.uid,
gid: input.runtimeIdentity.gid,
inventory: [...AGENT_PROFILE_INVENTORY],
profileId,
revisionId: desired.revisionId,
fence: desired.fence,
profileDigest: staged.hashes.profile,
});
const ownershipObserved = await input.ports.ownership.observe({
profileRoot: stagingRoot,
});
if (
!ownershipApplied?.ok ||
!validateRuntimeOwnership(ownershipObserved, input.runtimeIdentity)
) {
await input.ports.ownership.remove({
profileId,
profileRoot: stagingRoot,
slot: "STAGING",
revisionId: desired.revisionId,
fence: desired.fence,
});
return { ok: false, status: "OWNERSHIP_REJECTED" };
}
const secrets = input.reuseInstalledSecrets
? reuseInstalledRuntimeSecrets({
desired,
runtimeSecretsRoot,
mcpCredentialRoot: input.mcpCredentialRoot,
observation: input.reuseInstalledSecrets,
runtimeIdentity: input.runtimeIdentity,
})
: await materializeRuntimeSecrets({
desired,
runtimeSecretsRoot,
ports: input.ports,
serviceCredential:
input.serviceCredential ?? input.ports.serviceCredential,
runtimeIdentity: input.runtimeIdentity,
});
if (!secrets.ok) {
await input.ports.ownership.remove({
profileId,
profileRoot: stagingRoot,
slot: "STAGING",
revisionId: desired.revisionId,
fence: desired.fence,
});
if (!secrets.reused) cleanupSecrets(runtimeSecretsRoot, profileId);
return {
ok: false,
status: "SECRET_BINDING_REJECTED",
code: secrets.code,
};
}
reusedSecrets = secrets.reused === true;
if (input.prepareOnly !== true) {
const containment = await input.ports.containment.probe({
profileRoot: stagingRoot,
runtimeIdentity: input.runtimeIdentity,
workerIdentity: { workerId: desired.workerId },
serviceSecretFiles: secrets.files,
});
if (
!containment?.ok ||
containment.runtimeUid !== input.runtimeIdentity.uid ||
[
"siblingReadDenied",
"workerReadDenied",
"adminReadDenied",
"providerBytesDenied",
"hostMountDenied",
"dockerDenied",
"unexpectedToolDenied",
"wrongWorkspaceDispatchDenied",
].some((key) => containment[key] !== true)
) {
await input.ports.ownership.remove({
profileId,
profileRoot: stagingRoot,
slot: "STAGING",
revisionId: desired.revisionId,
fence: desired.fence,
});
if (!secrets.reused) {
cleanupSecrets(runtimeSecretsRoot, profileId);
await input.ports.credential.cleanup({ profileId });
}
return { ok: false, status: "CONTAINMENT_REJECTED" };
}
}
const hadPrior = existsSync(profileRoot);
if (
hadPrior &&
(input.reuseInstalledSecrets || input.priorKnownGoodObservation)
) {
priorObservation =
input.priorKnownGoodObservation ??
(await input.ports.gateway.observe({
profileId,
profileRoot,
priorGood: true,
}));
}
if (hadPrior)
writePromotionTransaction({
profilesRoot,
profileId,
desired,
targetProfileDigest: staged.hashes.profile,
reusedSecrets,
priorObservation,
authority,
});
if (hadPrior) renameSync(profileRoot, backupRoot);
if (hadPrior && input.checkpoint) await input.checkpoint("after-backup");
renameSync(stagingRoot, profileRoot);
fsyncDirectory(profilesRoot);
promoted = true;
if (input.checkpoint) await input.checkpoint("after-promote");
const active = staged;
if (input.prepareOnly === true) {
if (!hadPrior || !reusedSecrets || !existsSync(backupRoot)) {
throw new Error(
"prepared promotion requires prior-good bytes and reused secrets"
);
}
return {
ok: true,
status: "PREPARED",
profileId,
recoveredInterruptedPromotion,
backupRetained: true,
receipt: {
producer: "sellable-agent-profile-materializer",
subject: `agent-profile:${profileId}`,
revisionId: desired.revisionId,
profileDigest: active.hashes.profile,
xappFingerprint: secrets.xappFingerprint,
xoxbFingerprint: secrets.xoxbFingerprint,
serviceCredentialFingerprint: secrets.serviceCredentialFingerprint,
serviceCredentialGeneration: secrets.serviceCredentialGeneration,
},
};
}
const expected = expectedStaticObservation({
desired,
profileId,
validation: active,
secrets,
});
if (input.provisionOnly === true) {
if (hadPrior || existsSync(backupRoot)) {
throw new Error(
"initial provisioning cannot replace an existing profile"
);
}
return {
ok: true,
status: "PROVISIONED",
profileId,
recoveredInterruptedPromotion,
backupRetained: false,
observedLifecycle: "OFF",
observation: {
...expected,
revisionSequence: active.manifest.revisionSequence,
tools: [...desired.toolInclude],
process: null,
},
receipt: {
producer: "sellable-agent-profile-materializer",
subject: `agent-profile:${profileId}`,
revisionId: desired.revisionId,
profileDigest: active.hashes.profile,
xappFingerprint: secrets.xappFingerprint,
xoxbFingerprint: secrets.xoxbFingerprint,
serviceCredentialFingerprint: secrets.serviceCredentialFingerprint,
serviceCredentialGeneration: secrets.serviceCredentialGeneration,
},
};
}
startAttempted = true;
await input.ports.gateway.start({
profileId,
profileRoot,
identity: input.runtimeIdentity,
argv: ["hermes", "--profile", profileId, "gateway", "run"],
env: { HOME: "/profile", NODE_ENV: "production" },
slackDelivery: {
supervisorRunScript: "sellable-agent-runtime/run",
appFd: 8,
botFd: 9,
},
evidence: {
revisionId: desired.revisionId,
profileDigest: active.hashes.profile,
xappFingerprint: secrets.xappFingerprint,
xoxbFingerprint: secrets.xoxbFingerprint,
serviceCredentialFingerprint: secrets.serviceCredentialFingerprint,
serviceCredentialGeneration: secrets.serviceCredentialGeneration,
},
});
const observed = await input.ports.gateway.observe({
profileId,
profileRoot,
});
if (
!validateIndependentObservation(observed, expected, input.runtimeIdentity)
) {
throw new Error("gateway readback mismatch");
}
const tools = await input.ports.mcp.discoverTools({
profileId,
profileRoot,
});
if (stableJson(tools) !== stableJson(desired.toolInclude))
throw new Error("tool inventory mismatch");
const safeRead = await input.ports.mcp.safeRead({
profileId,
workspaceId: desired.workspaceId,
tool: desired.toolInclude[0],
});
if (!safeRead?.ok || safeRead.workspaceId !== desired.workspaceId)
throw new Error("safe read mismatch");
const backupRetained =
input.retainPriorGood === true && existsSync(backupRoot);
if (hadPrior) {
const committed = commitRetainedProfilePromotion({
...input,
targetObservation: observed,
});
if (!committed.ok) throw new Error("profile promotion commit unproven");
}
if (existsSync(backupRoot) && !backupRetained)
await input.ports.ownership.remove({
profileId,
profileRoot: backupRoot,
slot: "BACKUP",
revisionId: desired.revisionId,
fence: desired.fence,
});
if (hadPrior && !backupRetained) {
const finalized = finalizeRetainedProfilePromotion(input);
if (!finalized.ok) throw new Error("profile promotion finalize unproven");
}
return {
ok: true,
status: "READY",
profileId,
recoveredInterruptedPromotion,
backupRetained,
observation: {
...observed,
revisionSequence: active.manifest.revisionSequence,
tools: [...tools],
},
receipt: {
producer: "sellable-agent-profile-materializer",
subject: `agent-profile:${profileId}`,
revisionId: desired.revisionId,
profileDigest: active.hashes.profile,
xappFingerprint: secrets.xappFingerprint,
xoxbFingerprint: secrets.xoxbFingerprint,
serviceCredentialFingerprint: secrets.serviceCredentialFingerprint,
serviceCredentialGeneration: secrets.serviceCredentialGeneration,
},
};
} catch (error) {
if (error?.code === "SIMULATED_CRASH") {
return {
ok: false,
status: "CRASHED",
profileId,
recoveryPredicate: "backup_exists_and_active_profile_absent",
};
}
const outcome = await rollback({
profilesRoot,
profileRoot,
backupRoot,
runtimeSecretsRoot,
profileId,
desired,
ports: input.ports,
started: startAttempted,
preserveSecrets: reusedSecrets,
priorObservation,
runtimeIdentity: input.runtimeIdentity,
});
removePromotionTransaction(profilesRoot, profileId);
try {
if (existsSync(stagingRoot))
await input.ports.ownership.remove({
profileId,
profileRoot: stagingRoot,
slot: "STAGING",
revisionId: desired.revisionId,
fence: desired.fence,
});
} catch {}
return {
ok: false,
status:
outcome === "RESTORED_AND_RESTARTED"
? "ROLLED_BACK"
: outcome === "RESTORE_UNPROVEN"
? "ROLLBACK_UNPROVEN"
: "READBACK_REJECTED",
rolledBack: promoted,
rollback: { outcome },
};
}
}
export async function materializeAgentProfile(input = {}) {
const valid = validateDesired(input.desired);
const root = traversableProfilesRoot(input.profilesRoot);
if (!valid.ok || !root) return materializeAgentProfileLocked(input);
const profileId = deriveContainedProfileId(
input.desired.workspaceId,
input.desired.agentId,
input.desired.hostId
);
try {
return await withProfileSoulLock(
profileId,
() => materializeAgentProfileLocked(input),
{ dataRoot: basename(root) === "profiles" ? dirname(root) : root }
);
} catch {
return { ok: false, status: "LOCK_REJECTED", profileId };
}
}
export const NATIVE_SLACK_CUSTOMER_PROFILE_INVENTORY = Object.freeze([
SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER,
".env",
".no-bundled-skills",
"agent-profile.json",
"config.yaml",
"shell-hooks-allowlist.json",
"sellable/mcp.json",
"sellable/runtime-binding.json",
"SOUL.md",
"skills/sellable/SKILL.md",
]);
const LEGACY_INTERNAL_ADMIN_PROFILE_ROOT = "/opt/data/profiles/sellable-admin";
function repointInternalCronProfileReferences(desired, profileRoot) {
if (desired.workspaceId !== SELLABLE_INTERNAL_AGENT_WORKSPACE_ID) return;
const jobsPath = join(profileRoot, "cron", "jobs.json");
if (!existsSync(jobsPath)) return;
const entry = lstatSync(jobsPath);
if (
entry.isSymbolicLink() ||
!entry.isFile() ||
(entry.mode & 0o022) !== 0 ||
entry.size > 1024 * 1024
) {
throw new Error("native_slack_cron_jobs_rejected");
}
const current = readFileSync(jobsPath, "utf8");
JSON.parse(current);
if (!current.includes(LEGACY_INTERNAL_ADMIN_PROFILE_ROOT)) return;
const updated = current.replaceAll(
LEGACY_INTERNAL_ADMIN_PROFILE_ROOT,
`/opt/data/profiles/${desired.profileId}`
);
JSON.parse(updated);
atomicWrite(jobsPath, updated, entry.mode & 0o777);
}
const NATIVE_SLACK_KEYS = [
"runtimeMode",
"workspaceId",
"agentId",
"runtimeId",
"profileId",
"revisionId",
"revisionSequence",
"installationId",
"teamId",
"botUserId",
"slackAppGeneration",
"slackGeneration",
"routeGeneration",
"membershipGeneration",
"serviceCredentialGeneration",
"modelCredentialGeneration",
"modelActivationId",
"modelBundleGeneration",
"modelPolicyHash",
"apiServerKeyGeneration",
"apiServerKeyFingerprint",
"runtimeGeneration",
"selectedChannelIds",
"defaultChannelId",
"memberAllowlist",
"accessPolicyHash",
"runtimeDesiredHash",
"hermesVersion",
"installerPackage",
"mcpPackage",
"policyHash",
"customerProgramTimezone",
"soul",
"skill",
];
function isIanaTimezone(value) {
if (typeof value !== "string" || !value.trim()) return false;
try {
new Intl.DateTimeFormat("en", { timeZone: value }).format(new Date(0));
return true;
} catch {
return false;
}
}
function validateNativeSlackDesired(desired) {
if (!exactKeys(desired, NATIVE_SLACK_KEYS)) {
return { ok: false, code: "native_slack_profile_schema_invalid" };
}
if (
desired.runtimeMode !== "native_slack_customer" ||
!isIanaTimezone(desired.customerProgramTimezone) ||
[
"workspaceId",
"agentId",
"runtimeId",
"profileId",
"revisionId",
"installationId",
].some((key) => !SAFE_ID.test(desired[key] ?? "")) ||
!/^T[A-Z0-9]{8,20}$/.test(desired.teamId ?? "") ||
!/^[UW][A-Z0-9]{8,20}$/.test(desired.botUserId ?? "") ||
!Number.isSafeInteger(desired.revisionSequence) ||
desired.revisionSequence < 1 ||
[
"slackAppGeneration",
"slackGeneration",
"routeGeneration",
"membershipGeneration",
"serviceCredentialGeneration",
"modelCredentialGeneration",
"modelBundleGeneration",
"apiServerKeyGeneration",
"runtimeGeneration",
].some((key) => !Number.isSafeInteger(desired[key]) || desired[key] < 1) ||
desired.modelCredentialGeneration !== desired.modelBundleGeneration ||
!MODEL_AUTH_ACTIVATION_ID.test(desired.modelActivationId ?? "") ||
!SHA256.test(desired.modelPolicyHash ?? "") ||
!Array.isArray(desired.selectedChannelIds) ||
desired.selectedChannelIds.length < 1 ||
desired.selectedChannelIds.some(
(value) => !/^C[A-Z0-9]{8,20}$/.test(value)
) ||
stableJson([...new Set(desired.selectedChannelIds)].sort()) !==
stableJson(desired.selectedChannelIds) ||
!desired.selectedChannelIds.includes(desired.defaultChannelId) ||
!Array.isArray(desired.memberAllowlist) ||
desired.memberAllowlist.some(
(value) => !/^[UW][A-Z0-9]{8,20}$/.test(value)
) ||
stableJson([...new Set(desired.memberAllowlist)].sort()) !==
stableJson(desired.memberAllowlist) ||
!SHA256.test(desired.policyHash ?? "") ||
!SHA256.test(desired.accessPolicyHash ?? "") ||
!SHA256.test(desired.runtimeDesiredHash ?? "") ||
!FINGERPRINT.test(desired.apiServerKeyFingerprint ?? "") ||
desired.hermesVersion !== "0.21.5" ||
!/^@sellable\/install@\d+\.\d+\.\d+(?:[-.A-Za-z0-9]+)?$/.test(
desired.installerPackage ?? ""
) ||
!/^@sellable\/mcp@\d+\.\d+\.\d+(?:[-.A-Za-z0-9]+)?$/.test(
desired.mcpPackage ?? ""
) ||
typeof desired.soul !== "string" ||
typeof desired.skill !== "string" ||
RAW_SECRET.test(desired.soul) ||
RAW_SECRET.test(desired.skill)
) {
return { ok: false, code: "native_slack_profile_identity_invalid" };
}
return { ok: true };
}
function nativeSlackCustomerFiles(desired, soulBytes = desired.soul) {
const runtimeSecretRoot = `/run/sellable-agent/${desired.profileId}`;
const runtimeBinding = createMcpRuntimeBindingV2(desired);
const config = applyHermesSellableHostedPolicy({
profile: desired.profileId,
runtime: {
mode: desired.runtimeMode,
socketMode: true,
publicListener: false,
lazyInstall: false,
},
hermes: {
version: desired.hermesVersion,
optionalCapabilities: [],
runtimeInstallAllowed: false,
},
cron: {
wrap_response: false,
},
model: {
provider: FLY_CUSTOMER_MODEL_PROVIDER,
default: FLY_CUSTOMER_MODEL_ID,
reasoning_effort: FLY_CUSTOMER_MODEL_REASONING_EFFORT,
generation: desired.modelBundleGeneration,
activation_id: desired.modelActivationId,
policy_hash: desired.modelPolicyHash,
},
agent: {
reasoning_effort: FLY_CUSTOMER_MODEL_REASONING_EFFORT,
},
slack: {
require_mention: true,
strict_mention: false,
allow_bots: "none",
allowed_channels: [...desired.selectedChannelIds],
},
platforms: {
slack: {
enabled: true,
reply_to_mode: "all",
gateway_restart_notification: false,
typing_indicator: true,
},
},
platform_toolsets: { slack: [...SELLABLE_DEFAULT_SLACK_TOOLSETS] },
hooks: HERMES_SNAPSHOT_HOOKS,
mcp_servers: {
sellable: {
command:
"/usr/local/lib/sellable-agent/external-runtime/mcp/bin/sellable-mcp",
args: [],
enabled: true,
env: {
SELLABLE_CONFIG_PATH: `/opt/data/profiles/${desired.profileId}/sellable/mcp.json`,
SELLABLE_AGENT_SERVICE_CREDENTIAL_FILE: `${runtimeSecretRoot}/service-credential`,
SELLABLE_AGENT_PROFILE_ID: desired.profileId,
SELLABLE_AGENT_WORKSPACE_ID: desired.workspaceId,
SELLABLE_AGENT_ID: desired.agentId,
SELLABLE_AGENT_CREDENTIAL_GENERATION: String(
desired.serviceCredentialGeneration
),
SELLABLE_AGENT_POLICY_HASH: desired.policyHash,
SELLABLE_AGENT_SELECTED_CHANNEL_IDS: JSON.stringify(
desired.selectedChannelIds
),
SELLABLE_LOCK_WORKSPACE_ID: desired.workspaceId,
SELLABLE_REQUIRE_WORKSPACE_LOCK: "1",
},
},
...(desired.workspaceId === SELLABLE_INTERNAL_AGENT_WORKSPACE_ID
? {
"sellable-admin": {
command:
"/usr/local/lib/sellable-agent/internal-admin-mcp/node_modules/.bin/sellable-admin-mcp",
args: [],
enabled: true,
env: {
SELLABLE_ADMIN_HOME: `${runtimeSecretRoot}/admin-settings`,
SELLABLE_ADMIN_CONFIG_PATH: `${runtimeSecretRoot}/admin-settings/settings.json`,
SELLABLE_ADMIN_STATE_PATH: `${runtimeSecretRoot}/admin-settings/config-state.json`,
},
},
}
: {}),
},
});
const mcp = {
package: desired.mcpPackage,
workspaceLockId: desired.workspaceId,
requireWorkspaceLock: true,
policyHash: desired.policyHash,
runtimeMode: desired.runtimeMode,
transport: {
nativeHermesSessionContext: true,
trustedOuterMetadata: "_meta.sellableAgent",
processGlobalIdentityAllowed: false,
},
serviceCredential: {
file: `${runtimeSecretRoot}/service-credential`,
generation: desired.serviceCredentialGeneration,
},
requestChannelPolicy: {
source: "_meta.sellableAgent.channelId",
selectedChannelIds: [...desired.selectedChannelIds],
defaultChannelId: desired.defaultChannelId,
},
};
return {
[SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER]: "enabled\n",
".env": "",
// The runtime materializer owns this profile's exact skill bytes. Avoid
// Hermes re-scanning and re-copying its entire bundled catalog on every
// cold gateway start; explicit materialization remains the update path.
".no-bundled-skills": "",
"config.yaml": `${JSON.stringify(config, null, 2)}\n`,
"shell-hooks-allowlist.json": HERMES_SNAPSHOT_HOOK_ALLOWLIST_BYTES,
"sellable/mcp.json": `${JSON.stringify(mcp, null, 2)}\n`,
"sellable/runtime-binding.json": `${JSON.stringify(runtimeBinding, null, 2)}\n`,
"SOUL.md": soulBytes,
"skills/sellable/SKILL.md": desired.skill,
};
}
function nativeSlackManifest(desired, files) {
const fileHashes = Object.fromEntries(
Object.entries(files)
.sort(([left], [right]) => left.localeCompare(right))
.map(([logicalPath, bytes]) => [
logicalPath,
sha256(
logicalPath === "config.yaml"
? normalizedProfileConfigValueBytes(JSON.parse(bytes))
: bytes
),
])
);
const body = {
schemaVersion: "sellable-agent-native-slack-profile/v1",
runtimeMode: desired.runtimeMode,
workspaceId: desired.workspaceId,
agentId: desired.agentId,
runtimeId: desired.runtimeId,
profileId: desired.profileId,
revisionId: desired.revisionId,
revisionSequence: desired.revisionSequence,
installationId: desired.installationId,
teamId: desired.teamId,
botUserId: desired.botUserId,
slackAppGeneration: desired.slackAppGeneration,
slackGeneration: desired.slackGeneration,
routeGeneration: desired.routeGeneration,
membershipGeneration: desired.membershipGeneration,
serviceCredentialGeneration: desired.serviceCredentialGeneration,
modelCredentialGeneration: desired.modelCredentialGeneration,
modelActivationId: desired.modelActivationId,
modelBundleGeneration: desired.modelBundleGeneration,
modelPolicyHash: desired.modelPolicyHash,
apiServerKeyGeneration: desired.apiServerKeyGeneration,
apiServerKeyFingerprint: desired.apiServerKeyFingerprint,
runtimeGeneration: desired.runtimeGeneration,
installerPackage: desired.installerPackage,
mcpPackage: desired.mcpPackage,
hermesVersion: desired.hermesVersion,
policyHash: desired.policyHash,
accessPolicyHash: desired.accessPolicyHash,
runtimeDesiredHash: desired.runtimeDesiredHash,
fileHashes,
};
return { ...body, profileDigest: sha256(stableJson(body)) };
}
function renderNativeSlackCustomerProfile(root, desired, soulBytes) {
mkdirSync(root, { recursive: false, mode: 0o700 });
const files = nativeSlackCustomerFiles(desired, soulBytes);
for (const [logicalPath, bytes] of Object.entries(files)) {
atomicWrite(
join(root, logicalPath),
bytes,
logicalPath === "SOUL.md"
? 0o600
: logicalPath.endsWith(".md")
? 0o444
: 0o600
);
}
const manifest = nativeSlackManifest(desired, files);
atomicWrite(
join(root, "agent-profile.json"),
`${JSON.stringify(manifest, null, 2)}\n`,
0o600
);
fsyncDirectory(root);
return manifest;
}
export function validateNativeSlackCustomerProfile({ desired, profileRoot }) {
const valid = validateNativeSlackDesired(desired);
if (!valid.ok) return valid;
try {
if (
!isAbsolute(profileRoot) ||
!existsSync(profileRoot) ||
lstatSync(profileRoot).isSymbolicLink() ||
!lstatSync(profileRoot).isDirectory()
) {
return { ok: false, code: "native_slack_profile_path_invalid" };
}
// Runtime caches and live gateway coordination files are disposable and
// can contain large dependency trees, sockets, or symlinks. They are
// already excluded from profile replacement and snapshots; traversing
// them here only blocks the lifecycle heartbeat before replacement even
// starts. Keep the closed inventory walk for every durable path.
const found = inventory(profileRoot, { skipTransient: true });
const serverOwned = [...NATIVE_SLACK_CUSTOMER_PROFILE_INVENTORY].sort();
if (serverOwned.some((logicalPath) => !found.includes(logicalPath))) {
return { ok: false, code: "native_slack_profile_inventory_open" };
}
const soulPath = join(profileRoot, "SOUL.md");
const soulEntry = lstatSync(soulPath);
if (
soulEntry.isSymbolicLink() ||
!soulEntry.isFile() ||
(soulEntry.mode & 0o777) !== 0o600 ||
soulEntry.uid !== (process.getuid?.() ?? soulEntry.uid) ||
soulEntry.gid !== (process.getgid?.() ?? soulEntry.gid)
) {
return { ok: false, code: "native_slack_soul_file_rejected" };
}
const soulBytes = readFileSync(soulPath);
const files = nativeSlackCustomerFiles(desired, soulBytes);
for (const [logicalPath, expected] of Object.entries(files)) {
const pathValue = join(profileRoot, logicalPath);
const link = lstatSync(pathValue);
if (
link.isSymbolicLink() ||
!link.isFile() ||
(link.mode & 0o022) !== 0 ||
!(logicalPath === "config.yaml"
? normalizedProfileConfigBytes(pathValue).equals(
normalizedProfileConfigValueBytes(JSON.parse(expected))
)
: readFileSync(pathValue).equals(Buffer.from(expected)))
) {
return { ok: false, code: "native_slack_profile_file_drift" };
}
}
const manifest = JSON.parse(
readFileSync(join(profileRoot, "agent-profile.json"), "utf8")
);
const observedManifest = nativeSlackManifest(desired, files);
const normalizedManifest = {
...manifest,
fileHashes: {
...(manifest.fileHashes ?? {}),
"SOUL.md": observedManifest.fileHashes["SOUL.md"],
},
profileDigest: observedManifest.profileDigest,
};
if (stableJson(normalizedManifest) !== stableJson(observedManifest)) {
return { ok: false, code: "native_slack_profile_manifest_drift" };
}
const durableBytes = serverOwned
.filter((logicalPath) => logicalPath !== "SOUL.md")
.map((logicalPath) =>
readFileSync(join(profileRoot, logicalPath), "utf8")
)
.join("\n");
if (
RAW_SECRET.test(durableBytes) ||
/(?:0\.0\.0\.0|hostinger|multiplex|peer.?inventory|shared.?app.?token)/i.test(
durableBytes
)
) {
return { ok: false, code: "native_slack_profile_policy_rejected" };
}
return {
ok: true,
manifest: observedManifest,
revisionManifest: manifest,
observedSoulRawSha256: sha256(soulBytes),
};
} catch {
return { ok: false, code: "native_slack_profile_validation_failed" };
}
}
export function materializeNativeSlackCustomerProfile({
desired,
profilesRoot,
diskFreeBytes = filesystemAvailableBytes,
}) {
const valid = validateNativeSlackDesired(desired);
if (!valid.ok) return valid;
const root = traversableProfilesRoot(profilesRoot);
if (!root) {
return { ok: false, code: "native_slack_profiles_root_rejected" };
}
const activeRoot = join(root, desired.profileId);
const stagingRoot = join(
root,
`.${desired.profileId}.${randomUUID()}.staging`
);
const backupRoot = join(root, `.${desired.profileId}.materializer-backup`);
const materialize = () => {
try {
if (dirname(activeRoot) !== root || dirname(stagingRoot) !== root) {
return { ok: false, code: "native_slack_profile_path_rejected" };
}
if (existsSync(backupRoot)) {
const backup = lstatSync(backupRoot);
if (backup.isSymbolicLink() || !backup.isDirectory()) {
throw new Error("native_slack_profile_backup_rejected");
}
if (!existsSync(activeRoot)) renameSync(backupRoot, activeRoot);
else {
const staleBackupRoot = join(
root,
`.${desired.profileId}.${randomUUID()}.cleanup`
);
renameSync(backupRoot, staleBackupRoot);
scheduleNativeSlackTreeRemoval(root, staleBackupRoot);
}
}
sweepStaleNativeSlackTrees(root, desired.profileId);
const extraProfiles = readdirSync(root).filter((name) => {
if (name === desired.profileId || name.startsWith(".")) return false;
const pathValue = join(root, name);
const entry = lstatSync(pathValue);
return entry.isDirectory() || entry.isSymbolicLink();
});
if (extraProfiles.length > 0) {
return {
ok: false,
code: "native_slack_extra_profile_rejected",
extraProfiles: extraProfiles.sort(),
};
}
if (existsSync(activeRoot)) {
if (lstatSync(activeRoot).isSymbolicLink()) {
throw new Error("active profile symlink rejected");
}
repointInternalCronProfileReferences(desired, activeRoot);
const reusable = validateNativeSlackCustomerProfile({
desired,
profileRoot: activeRoot,
});
if (reusable.ok) {
return {
ok: true,
status: "REUSED",
profileId: desired.profileId,
profileDigest: reusable.manifest.profileDigest,
observedSoulRawSha256: reusable.observedSoulRawSha256,
};
}
}
const desiredSeedBytes = Object.values(
nativeSlackCustomerFiles(desired)
).reduce(
(total, bytes) => total + Buffer.byteLength(bytes),
Buffer.byteLength(JSON.stringify(nativeSlackManifest(desired, {})))
);
// Replacement links durable same-volume state into staging, so it does
// not need another full copy of the profile. Reserve only the managed
// seed plus a bounded directory/metadata margin.
const profileBytes = desiredSeedBytes;
const requiredBytes =
profileBytes + NATIVE_SLACK_MATERIALIZATION_MARGIN_BYTES;
let availableBytes = Number(diskFreeBytes(root));
if (!Number.isFinite(availableBytes) || availableBytes < 0) {
throw new Error("native_slack_profile_disk_readback_invalid");
}
if (availableBytes < requiredBytes) {
prunePressureCaches(root);
availableBytes = Number(diskFreeBytes(root));
}
if (availableBytes < requiredBytes) {
return {
ok: false,
code: "native_slack_profile_insufficient_disk",
requiredBytes,
availableBytes,
profileBytes,
};
}
let soulBytes = Buffer.from(desired.soul, "utf8");
// A workspace snapshot can predate the dedicated native-Slack markers.
// Materialization intentionally adds the one-time safe default toolsets
// to that legacy profile. Normalize the source identity to the same
// target representation so this migration is not mistaken for lost
// live state while explicit legacy Slack toolsets remain exact.
const liveStateIdentity = preservedLiveStateIdentity(activeRoot, {
normalizeLegacyNativeDefault: true,
});
if (existsSync(activeRoot)) {
const soulPath = join(activeRoot, "SOUL.md");
if (existsSync(soulPath)) {
const soulEntry = lstatSync(soulPath);
if (soulEntry.isSymbolicLink() || !soulEntry.isFile()) {
throw new Error("active soul rejected");
}
soulBytes = readFileSync(soulPath);
}
}
const manifest = renderNativeSlackCustomerProfile(
stagingRoot,
desired,
soulBytes
);
linkHermesState(activeRoot, stagingRoot);
repointInternalCronProfileReferences(desired, stagingRoot);
mergeLiveMutableConfig(activeRoot, stagingRoot);
if (
liveStateIdentity !== null &&
preservedLiveStateIdentity(stagingRoot, {
normalizeLegacyNativeDefault: true,
}) !== liveStateIdentity
) {
throw new Error("live mutable state identity changed during staging");
}
const staged = validateNativeSlackCustomerProfile({
desired,
profileRoot: stagingRoot,
});
if (!staged.ok) throw new Error(staged.code);
if (existsSync(activeRoot)) {
if (lstatSync(activeRoot).isSymbolicLink()) {
throw new Error("active profile symlink rejected");
}
renameSync(activeRoot, backupRoot);
}
renameSync(stagingRoot, activeRoot);
fsyncDirectory(root);
if (existsSync(backupRoot)) {
const cleanupRoot = join(
root,
`.${desired.profileId}.${randomUUID()}.cleanup`
);
renameSync(backupRoot, cleanupRoot);
scheduleNativeSlackTreeRemoval(root, cleanupRoot);
}
return {
ok: true,
status: "MATERIALIZED",
profileId: desired.profileId,
profileDigest: manifest.profileDigest,
observedSoulRawSha256: sha256(soulBytes),
};
} catch (error) {
if (existsSync(stagingRoot) && !lstatSync(stagingRoot).isSymbolicLink()) {
const cleanupRoot = join(
root,
`.${desired.profileId}.${randomUUID()}.cleanup`
);
renameSync(stagingRoot, cleanupRoot);
scheduleNativeSlackTreeRemoval(root, cleanupRoot);
}
if (!existsSync(activeRoot) && existsSync(backupRoot)) {
renameSync(backupRoot, activeRoot);
}
return {
ok: false,
code: "native_slack_profile_materialization_failed",
causeCode: materializationCauseCode(error),
};
}
};
try {
return withProfileSoulLock(desired.profileId, materialize, {
dataRoot: basename(root) === "profiles" ? dirname(root) : root,
});
} catch (error) {
return {
ok: false,
code: "native_slack_profile_materialization_failed",
causeCode: materializationCauseCode(error),
};
}
}
export const NATIVE_SLACK_ADMIN_PROFILE_INVENTORY = Object.freeze([
"agent-profile.json",
"config.yaml",
"sellable/mcp.json",
"sellable/runtime-binding.json",
"SOUL.md",
"skills/sellable/SKILL.md",
]);
export const ADMIN_SETTINGS_FILE_INVENTORY = Object.freeze([
".env",
"settings.json",
"config-state.json",
]);
const ADMIN_SETTINGS_CORE_FILES = Object.freeze([
".env",
"settings.json",
"config-state.json",
]);
export function classifyAdminSettingsReadiness({
settingsDirectory,
filePaths = {},
}) {
const root = privateRoot(settingsDirectory);
if (
!root ||
(lstatSync(root).mode & 0o777) !== 0o700 ||
Object.keys(filePaths).some(
(name) => !ADMIN_SETTINGS_FILE_INVENTORY.includes(name)
)
) {
return { ok: false, code: "admin_settings_root_or_inventory_invalid" };
}
const present = new Set();
for (const name of ADMIN_SETTINGS_FILE_INVENTORY) {
const candidate = filePaths[name] ?? join(root, name);
if (!isAbsolute(candidate)) {
return {
ok: false,
code: "admin_settings_file_containment_invalid",
name,
};
}
if (!existsSync(candidate)) continue;
let parent;
let entry;
try {
parent = realpathSync(dirname(candidate));
entry = lstatSync(candidate);
} catch {
return { ok: false, code: "admin_settings_file_type_invalid", name };
}
if (parent !== root || basename(candidate) !== name) {
return {
ok: false,
code: "admin_settings_file_containment_invalid",
name,
};
}
if (entry.isSymbolicLink() || !entry.isFile()) {
return { ok: false, code: "admin_settings_file_type_invalid", name };
}
if ((entry.mode & 0o777) !== 0o600) {
return { ok: false, code: "admin_settings_file_mode_invalid", name };
}
present.add(name);
}
const missing = ADMIN_SETTINGS_FILE_INVENTORY.filter(
(name) => !present.has(name)
);
return {
ok: true,
complete: missing.length === 0,
coreReady: ADMIN_SETTINGS_CORE_FILES.every((name) => present.has(name)),
missing,
};
}
export function selectSellableAgentRuntimeImage({
kind,
generation,
customerImage,
}) {
if (kind === "CUSTOMER") {
if (generation != null)
throw new Error("customer_admin_generation_rejected");
return { kind, image: customerImage, namespace: "customer" };
}
if (
kind !== "ADMIN" ||
generation?.agentKind !== "ADMIN" ||
generation?.state !== "ACTIVE" ||
generation?.adminImageDigest == null
)
throw new Error("admin_runtime_generation_required");
return {
kind,
image: generation.adminImageDigest,
namespace: `admin:${generation.agentId}`,
};
}
export function buildNativeSlackAdminMcpProfile({
customerConfig,
generation,
settingsDirectory = "/run/sellable-agent/admin-settings",
}) {
if (
generation?.agentKind !== "ADMIN" ||
generation?.state !== "ACTIVE" ||
!isAbsolute(settingsDirectory) ||
(lstatSync(settingsDirectory).mode & 0o777) !== 0o700
)
throw new Error("admin_profile_generation_rejected");
const sellable = customerConfig?.mcp_servers?.sellable;
if (
!sellable ||
!isAbsolute(sellable.command) ||
Array.isArray(sellable.args) === false ||
sellable.args.length !== 0
)
throw new Error("customer_product_mcp_rejected");
const adminCommand =
"/usr/local/lib/sellable-agent/admin-runtime/admin-mcp/bin/sellable-admin-mcp";
return {
...customerConfig,
mcp_servers: {
sellable: structuredClone(sellable),
"sellable-admin": {
command: adminCommand,
args: [],
enabled: true,
env: { SELLABLE_ADMIN_SETTINGS_DIR: settingsDirectory },
},
},
};
}
/**
* @param {{
* profile: any;
* observedServers: readonly string[];
* expectedTools?: Record<string, readonly string[]>;
* observedTools: Record<string, readonly string[]>;
* profileContract?: any;
* }} input
*/
export function validateClosedAdminMcpInventory({
profile,
observedServers,
expectedTools,
observedTools,
profileContract,
}) {
if (profileContract !== undefined) {
const { digest: contractDigest, ...unsignedContract } =
profileContract ?? {};
const packages = [
profileContract?.packages?.sellable?.mcp,
profileContract?.packages?.sellable?.install,
profileContract?.packages?.["sellable-admin"]?.mcp,
profileContract?.packages?.["sellable-admin"]?.install,
];
if (
profileContract?.schemaVersion !== "sellable-admin-profile-contract/v1" ||
!/^\/opt\/data\/profiles\/[A-Za-z0-9_-]{1,128}$/.test(
profileContract?.profileHome ?? ""
) ||
stableJson(profileContract?.aliases) !==
stableJson(["sellable", "sellable-admin"]) ||
!SHA256.test(profileContract?.contentDigest ?? "") ||
!SHA256.test(contractDigest ?? "") ||
sha256(stableJson(unsignedContract)) !== contractDigest ||
packages.some(
(entry) =>
typeof entry?.name !== "string" ||
!entry.name ||
typeof entry?.version !== "string" ||
!entry.version ||
!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(entry?.integrity ?? "") ||
!SHA256.test(entry?.contentSha256 ?? "")
) ||
!Array.isArray(profileContract?.tools?.sellable) ||
!Array.isArray(profileContract?.tools?.["sellable-admin"])
) {
return { ok: false, code: "admin_profile_contract_invalid" };
}
expectedTools = profileContract.tools;
}
const names = Object.keys(profile?.mcp_servers ?? {}).sort();
const expectedNames = profileContract?.aliases ?? [
"sellable",
"sellable-admin",
];
if (stableJson(names) !== stableJson(expectedNames))
return { ok: false, code: "admin_mcp_server_inventory_open" };
if (stableJson([...observedServers].sort()) !== stableJson(names))
return { ok: false, code: "admin_mcp_server_readback_drift" };
for (const name of names) {
const command = profile.mcp_servers[name]?.command;
if (
!isAbsolute(command ?? "") ||
/(?:@latest|npm|npx|sh|bash)/.test(command) ||
stableJson([...(observedTools[name] ?? [])].sort()) !==
stableJson([...(expectedTools[name] ?? [])].sort())
)
return { ok: false, code: "admin_mcp_inventory_or_command_drift" };
}
return { ok: true };
}
export function verifyExactAdminProfileContract({ expected, actual }) {
if (
expected?.schemaVersion !== "sellable-admin-profile-contract/v1" ||
!SHA256.test(expected?.digest ?? "") ||
stableJson(expected) !== stableJson(actual)
) {
throw new Error("admin_profile_contract_mismatch");
}
const { digest: expectedDigest, ...unsigned } = expected;
if (sha256(stableJson(unsigned)) !== expectedDigest) {
throw new Error("admin_profile_contract_digest_invalid");
}
return structuredClone(expected);
}