feat(ito): expose guarded live node qualification

Expose the canonical Itō CLI's pinned sixtytwo node-qualification path through ECC with double opt-in, explicit node/config gates, credential isolation, and no new MCP or execution authority.

Validated across the full Linux, macOS, and Windows Node/package-manager matrix, hosted coverage, CodeQL, security, lint, and focused bridge tests.
This commit is contained in:
Affaan Mustafa 2026-07-23 22:10:57 -07:00 committed by GitHub
parent 34fbe007f0
commit 33c7dbb7d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 384 additions and 42 deletions

View file

@ -4,7 +4,7 @@ const { spawnSync } = require('child_process');
const path = require('path');
const { listAvailableLanguages } = require('./lib/install-executor');
const { getComputeSponsorCopy } = require('./lib/compute-sponsor');
const { createSafeItoEnvironment } = require('./lib/ito-environment');
const { createSafeItoInvocationEnvironment } = require('./lib/ito-environment');
const COMMANDS = {
install: {
@ -107,7 +107,7 @@ const PRIMARY_COMMANDS = [
];
function showHelp(exitCode = 0) {
console.log(`
process.stdout.write(`
ECC selective-install CLI
Usage:
@ -141,6 +141,7 @@ Examples:
ecc ito auth
ecc ito find --gpu h200 --count 8 --nodes 1 --gpus-per-node 8 --days 30 --storage-tb 1 --start-window 2099-08-15 --max-rate 3.00 --form-factor bare_metal --contract-type reservation --fabric infiniband --region us-east-1
ecc ito status --json
ecc ito evals --cluster clu_prod_example --live-sixtytwo --nodes gpu-01,gpu-02 --config-dir /absolute/path/to/qualification-config
ecc list-installed --json
ecc doctor --target cursor
ecc repair --dry-run
@ -226,7 +227,6 @@ function runCommand(commandName, args) {
if (!command) {
throw new Error(`Unknown command: ${commandName}`);
}
const result = spawnSync(
process.execPath,
[path.join(__dirname, command.script), ...args],
@ -234,9 +234,8 @@ function runCommand(commandName, args) {
cwd: process.cwd(),
env: commandName === 'ito'
? {
...createSafeItoEnvironment(process.env, {
...createSafeItoInvocationEnvironment(process.env, args, {
includeControls: true,
includeItoRuntime: true,
}),
}
: process.env,

View file

@ -5,9 +5,12 @@
const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const { createSafeItoEnvironment } = require("./lib/ito-environment");
const {
createSafeItoInvocationEnvironment,
getInvocationCommand,
} = require("./lib/ito-environment");
const SUPPORTED_COMMANDS = Object.freeze(["auth", "find", "status"]);
const SUPPORTED_COMMANDS = Object.freeze(["auth", "find", "status", "evals"]);
const CANONICAL_REPOSITORY = "https://github.com/Ito-Markets/ito-cloud-runtime.git";
const CANONICAL_PACKAGE_PATH = "cli/ito-compute-cli";
const CANONICAL_ENTRY_SEGMENTS = Object.freeze([
@ -18,25 +21,30 @@ const CANONICAL_ENTRY_SEGMENTS = Object.freeze([
]);
const EXECUTABLE_OVERRIDE = "ECC_ITO_CLI_EXECUTABLE";
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
const NODE_QUALIFICATION_TIMEOUT_MS = 31 * 60 * 1000;
function showHelp() {
console.log(`
process.stdout.write(`
ECC × Itô local CLI bridge
Usage:
ecc ito auth
ecc ito find <all required RFQ options>
ecc ito status
ecc ito <auth|find|status> --json
ecc ito evals --cluster <id> --live-sixtytwo --nodes <list> --config-dir <dir>
ecc ito <auth|find|status|evals> --json
The bridge invokes the separately installed canonical Itô CLI and returns its
real stdout, stderr, and exit code unchanged. It performs no browser navigation
and adds no lock, workload, inference, evaluation, or purchase path.
and adds no lock, workload, inference, or purchase path.
Important:
- "find" reads live inventory and submits an authenticated RFQ.
- Obtain explicit buyer authority and every hard constraint before invoking it.
- "status" reads live RFQ and procurement status.
- "evals" invokes only the canonical CLI's double-opt-in, pinned
sixtytwo-cli node-qualification adapter against explicit nodes.
- Node qualification cannot rent, launch, recover, repair, or purchase.
- Inventory and RFQs are not reservations; only a returned firm quote is firm.
The canonical package is currently unpublished. Install it locally:
@ -59,11 +67,67 @@ The same package's MCP server exposes only:
Configure the MCP command as "node" with this absolute argument:
/absolute/path/to/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}/dist/bin/ito-mcp.js
Inject ITO_API_KEY into the child process from 1Password or the launching
environment. Never put the key in arguments, tracked files, or chat.
For auth, find, and status, inject ITO_API_KEY into the child process from
1Password or the launching environment. Never put the key in arguments,
tracked files, or chat.
Live node qualification requires ITO_ENABLE_SIXTYTWO_LIVE=1,
--live-sixtytwo, an explicit node list, and an existing absolute config
directory. It forwards only named SIXTYTWO_API_TOKEN/SIXTYTWO_TOKEN and SSH
agent state; ITO_API_KEY is intentionally excluded. The canonical CLI requires
sixtytwo-cli==0.3.33 and fails closed.
`);
}
function requiredOptionValue(args, option) {
const indexes = args
.map((value, index) => (value === option ? index : -1))
.filter((index) => index >= 0);
if (indexes.length !== 1) {
throw new Error(`${option} is required exactly once for live node qualification.`);
}
const value = args[indexes[0] + 1];
if (!value?.trim() || value.startsWith("--")) {
throw new Error(`${option} requires a non-empty value for live node qualification.`);
}
return value;
}
function validateNodeQualificationArgs(args, environment) {
if (environment.ITO_ENABLE_SIXTYTWO_LIVE !== "1") {
throw new Error(
"Live node qualification requires ITO_ENABLE_SIXTYTWO_LIVE=1 before any process is started."
);
}
if (args.filter((value) => value === "--live-sixtytwo").length !== 1) {
throw new Error(
"Live node qualification requires --live-sixtytwo exactly once before any process is started."
);
}
requiredOptionValue(args, "--cluster");
const nodes = requiredOptionValue(args, "--nodes");
if (!nodes.split(",").every((node) => node.trim().length > 0)) {
throw new Error("--nodes must explicitly list one or more non-empty nodes.");
}
const configDirectory = requiredOptionValue(args, "--config-dir");
if (!path.isAbsolute(configDirectory)) {
throw new Error("--config-dir must be an existing absolute directory.");
}
try {
const resolved = fs.realpathSync.native(configDirectory);
if (
!fs.statSync(resolved).isDirectory()
|| !fs.statSync(path.join(resolved, "sixtytwo.yaml")).isFile()
) {
throw new Error("invalid qualification configuration");
}
} catch {
throw new Error(
"--config-dir must exist and contain a regular sixtytwo.yaml before any process is started."
);
}
}
function parseArgs(argv, environment = process.env) {
const args = [...argv];
if (
@ -90,9 +154,12 @@ function parseArgs(argv, environment = process.env) {
const command = withoutJson.shift();
if (!SUPPORTED_COMMANDS.includes(command)) {
throw new Error(
`Unsupported Itô command "${command || "(missing)"}"; ECC permits only auth, find, and status.`
`Unsupported Itô command "${command || "(missing)"}"; ECC permits only auth, find, status, and evals.`
);
}
if (command === "evals") {
validateNodeQualificationArgs(withoutJson, environment);
}
return Object.freeze({
help: false,
@ -186,13 +253,16 @@ function buildInvocation(executable, args) {
function invokeIto(executable, args, environment = process.env) {
const invocation = buildInvocation(executable, args);
const command = getInvocationCommand(args);
const isNodeQualification = command === "evals";
const result = spawnSync(invocation.executable, invocation.args, {
cwd: process.cwd(),
encoding: "utf8",
env: {
...createSafeItoEnvironment(environment, { includeItoRuntime: true }),
},
// Keep policy helpers immutable for callers, but give child-process
// instrumentation its own mutable copy (for example NODE_V8_COVERAGE).
env: { ...createSafeItoInvocationEnvironment(environment, args) },
maxBuffer: MAX_OUTPUT_BYTES,
timeout: isNodeQualification ? NODE_QUALIFICATION_TIMEOUT_MS : undefined,
shell: false,
windowsHide: true,
});
@ -232,6 +302,7 @@ module.exports = Object.freeze({
CANONICAL_PACKAGE_PATH,
CANONICAL_REPOSITORY,
EXECUTABLE_OVERRIDE,
NODE_QUALIFICATION_TIMEOUT_MS,
SUPPORTED_COMMANDS,
buildInvocation,
invokeIto,

View file

@ -29,11 +29,20 @@ const ITO_RUNTIME_ENVIRONMENT_KEYS = Object.freeze([
"ITO_INVENTORY_URL",
]);
const ITO_EVAL_ENVIRONMENT_KEYS = Object.freeze([
"ITO_ENABLE_SIXTYTWO_LIVE",
"SIXTYTWO_API_TOKEN",
"SIXTYTWO_TOKEN",
"SSH_AUTH_SOCK",
"SSH_AGENT_PID",
]);
const ECC_ITO_CONTROL_KEYS = Object.freeze([
"ECC_DRY_RUN",
"ECC_ITO_CLI_EXECUTABLE",
"NODE_ENV",
]);
const ITO_RUNTIME_COMMANDS = new Set(["auth", "find", "status"]);
function copyDefined(source, target, key) {
if (typeof source[key] === "string") {
@ -56,6 +65,12 @@ function createSafeItoEnvironment(source = process.env, options = {}) {
}
}
if (options.includeItoEvals) {
for (const key of ITO_EVAL_ENVIRONMENT_KEYS) {
copyDefined(source, safe, key);
}
}
if (options.includeControls) {
for (const key of ECC_ITO_CONTROL_KEYS) {
copyDefined(source, safe, key);
@ -65,9 +80,29 @@ function createSafeItoEnvironment(source = process.env, options = {}) {
return Object.freeze(safe);
}
function getInvocationCommand(args = []) {
return args.filter((value) => value !== "--json")[0];
}
function createSafeItoInvocationEnvironment(
source = process.env,
args = [],
options = {},
) {
const command = getInvocationCommand(args);
return createSafeItoEnvironment(source, {
includeControls: options.includeControls === true,
includeItoRuntime: ITO_RUNTIME_COMMANDS.has(command),
includeItoEvals: command === "evals",
});
}
module.exports = Object.freeze({
ECC_ITO_CONTROL_KEYS,
ITO_EVAL_ENVIRONMENT_KEYS,
ITO_RUNTIME_ENVIRONMENT_KEYS,
SYSTEM_ENVIRONMENT_KEYS,
createSafeItoEnvironment,
createSafeItoInvocationEnvironment,
getInvocationCommand,
});