feat: add pinned Nasiko control-plane bridge

This commit is contained in:
Affaan Mustafa
2026-08-15 01:57:26 -04:00
parent f9801fb16b
commit 0d39ae83dd
8 changed files with 554 additions and 0 deletions
+8
View File
@@ -202,6 +202,14 @@
"ito-compute" "ito-compute"
] ]
}, },
{
"id": "capability:nasiko-control-plane",
"family": "capability",
"description": "Explicitly gated Nasiko control-plane installation, status, and agent-operations guidance with pinned artifact verification and opt-in telemetry boundaries.",
"modules": [
"nasiko-control-plane"
]
},
{ {
"id": "capability:social", "id": "capability:social",
"family": "capability", "family": "capability",
+30
View File
@@ -635,6 +635,36 @@
"cost": "light", "cost": "light",
"stability": "beta" "stability": "beta"
}, },
{
"id": "nasiko-control-plane",
"kind": "skills",
"description": "Explicitly gated Nasiko control-plane installation, status, and agent-operations guidance with pinned artifact verification and opt-in telemetry boundaries.",
"paths": [
"skills/nasiko-control-plane"
],
"targets": [
"claude",
"claude-project",
"cursor",
"antigravity",
"codex",
"gemini",
"opencode",
"codebuddy",
"joycode",
"qwen",
"zed",
"hermes",
"openclaw",
"kimi"
],
"dependencies": [
"platform-configs"
],
"defaultInstall": false,
"cost": "light",
"stability": "experimental"
},
{ {
"id": "social-distribution", "id": "social-distribution",
"kind": "skills", "kind": "skills",
+3
View File
@@ -118,6 +118,8 @@
"scripts/install-guided.js", "scripts/install-guided.js",
"scripts/install-plan.js", "scripts/install-plan.js",
"scripts/ito.js", "scripts/ito.js",
"scripts/nasiko.js",
"scripts/lib/nasiko-release.js",
"scripts/lib/", "scripts/lib/",
"scripts/list-installed.js", "scripts/list-installed.js",
"scripts/loop-status.js", "scripts/loop-status.js",
@@ -233,6 +235,7 @@
"skills/ito-compute/", "skills/ito-compute/",
"skills/ito-inference/", "skills/ito-inference/",
"skills/ito-training/", "skills/ito-training/",
"skills/nasiko-control-plane/",
"skills/investor-materials/", "skills/investor-materials/",
"skills/investor-outreach/", "skills/investor-outreach/",
"skills/iterative-retrieval/", "skills/iterative-retrieval/",
+8
View File
@@ -39,6 +39,10 @@ const COMMANDS = {
script: 'ito.js', script: 'ito.js',
description: 'Invoke the separately installed canonical Itô compute CLI', description: 'Invoke the separately installed canonical Itô compute CLI',
}, },
nasiko: {
script: 'nasiko.js',
description: 'Install or inspect the optional pinned Nasiko control-plane CLI',
},
memory: { memory: {
script: 'memory.js', script: 'memory.js',
description: 'Share durable context across Claude, Codex, Hermes, and other harnesses', description: 'Share durable context across Claude, Codex, Hermes, and other harnesses',
@@ -110,6 +114,7 @@ const PRIMARY_COMMANDS = [
'consult', 'consult',
'control-pane', 'control-pane',
'ito', 'ito',
'nasiko',
'memory', 'memory',
'list-installed', 'list-installed',
'doctor', 'doctor',
@@ -168,6 +173,9 @@ Examples:
ecc ito auth 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 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 status --json
ecc nasiko status --json
ecc nasiko install --version v0.1.0 --dry-run --json
ecc nasiko install --version v0.1.0 --yes --json
ecc ito evals --cluster clu_prod_example --live-sixtytwo --nodes gpu-01,gpu-02 --config-dir /absolute/path/to/qualification-config ecc ito evals --cluster clu_prod_example --live-sixtytwo --nodes gpu-01,gpu-02 --config-dir /absolute/path/to/qualification-config
ecc memory init ecc memory init
ecc memory handoff --from codex --target claude --title "Continue migration" --stdin ecc memory handoff --from codex --target claude --title "Continue migration" --stdin
+323
View File
@@ -0,0 +1,323 @@
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const https = require('https');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');
const REGISTRY_ORIGIN = 'https://registry.nasiko.dev';
const REPOSITORY = 'nasiko/nasiko';
const MAX_MANIFEST_BYTES = 1024 * 1024;
const MAX_ARCHIVE_BYTES = 100 * 1024 * 1024;
const SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/;
const QUALIFIED_RELEASES = Object.freeze({
'v0.1.0': Object.freeze({
'linux/amd64': 'sha256:0df748a40f3d714b6b6a3376a1d13a224c05bdb8d1628f31ace5a7bee8ceb9de',
'linux/arm64': 'sha256:655021a129c7df4621a80d16ea4eab38018530bfe9a20da646641d9f4ac5c249',
'darwin/amd64': 'sha256:b4188482621efd7da5a2ab630f653665ab5f80b9aceae448b6bb5fc93e003f06',
'darwin/arm64': 'sha256:ce7e54fa19f989a5d125c4409b3587ca9503bb5a07bc5ff223c60e0fbad437f0',
'windows/amd64': 'sha256:0760fe1fc98e8fedb66796aaf891a1de9268af1338c5e88b949656fda5d9f045',
}),
});
function normalizePlatform(platform = process.platform, architecture = process.arch) {
const osName = platform === 'win32' ? 'windows' : platform;
if (!['linux', 'darwin', 'windows'].includes(osName)) {
throw new Error(`Unsupported platform: ${platform}`);
}
const arch = architecture === 'x64' ? 'amd64' : architecture;
if (!['amd64', 'arm64'].includes(arch)) {
throw new Error(`Unsupported architecture: ${architecture}`);
}
if (osName === 'windows' && arch !== 'amd64') {
throw new Error(`Unsupported architecture for Windows: ${architecture}`);
}
return {
os: osName,
arch,
binaryName: osName === 'windows' ? 'nasiko.exe' : 'nasiko',
};
}
function getQualifiedRelease(version, platform = process.platform, architecture = process.arch) {
if (!/^v\d+\.\d+\.\d+$/.test(String(version || ''))) {
throw new Error('Nasiko installation requires a pinned version such as v0.1.0; latest is not allowed.');
}
const release = QUALIFIED_RELEASES[version];
if (!release) {
throw new Error(`Nasiko ${version} is not qualified by this ECC release.`);
}
const normalized = normalizePlatform(platform, architecture);
const manifestDigest = release[`${normalized.os}/${normalized.arch}`];
if (!manifestDigest) {
throw new Error(`Nasiko ${version} is not qualified for ${normalized.os}/${normalized.arch}.`);
}
return { version, ...normalized, manifestDigest };
}
function digestBytes(bytes) {
return `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;
}
function assertDigest(bytes, expectedDigest, label) {
if (!SHA256_PATTERN.test(expectedDigest)) {
throw new Error(`${label} has an invalid expected digest.`);
}
const actualDigest = digestBytes(bytes);
if (actualDigest !== expectedDigest) {
throw new Error(`${label} digest mismatch: expected ${expectedDigest}, got ${actualDigest}.`);
}
}
function validateManifest(manifestBytes) {
let manifest;
try {
manifest = JSON.parse(manifestBytes.toString('utf8'));
} catch (_error) {
throw new Error('Nasiko manifest is not valid JSON.');
}
if (manifest.schemaVersion !== 2 || !Array.isArray(manifest.layers) || manifest.layers.length !== 1) {
throw new Error('Nasiko manifest must contain exactly one OCI layer.');
}
const layer = manifest.layers[0];
if (layer.mediaType !== 'application/gzip' || !SHA256_PATTERN.test(layer.digest)) {
throw new Error('Nasiko manifest layer is not a qualified gzip artifact.');
}
if (!Number.isSafeInteger(layer.size) || layer.size <= 0 || layer.size > MAX_ARCHIVE_BYTES) {
throw new Error('Nasiko manifest layer size is outside the allowed range.');
}
return { digest: layer.digest, size: layer.size };
}
function validateArchiveEntries(entries, expectedBinaryName) {
if (!Array.isArray(entries) || entries.length !== 1) {
throw new Error('Unsafe archive: expected exactly one binary file.');
}
const [entry] = entries;
const normalizedPath = String(entry.path || '').replace(/^\.\//, '');
if (normalizedPath !== expectedBinaryName || normalizedPath.includes('..') || path.isAbsolute(normalizedPath)) {
throw new Error('Unsafe archive path: expected only the Nasiko binary.');
}
if (entry.type !== 'file') {
throw new Error('Nasiko archive entry must be a regular file.');
}
return true;
}
function fetchBytes(url, options = {}) {
const maxBytes = options.maxBytes || MAX_ARCHIVE_BYTES;
const timeoutMs = options.timeoutMs || 15000;
const parsed = new URL(url);
if (parsed.origin !== REGISTRY_ORIGIN || parsed.protocol !== 'https:') {
return Promise.reject(new Error('Nasiko download origin is not allowed.'));
}
return new Promise((resolve, reject) => {
const request = https.get(parsed, {
headers: options.accept ? { Accept: options.accept } : {},
}, response => {
if (response.statusCode >= 300 && response.statusCode < 400) {
response.resume();
reject(new Error('Nasiko registry redirects are not allowed.'));
return;
}
if (response.statusCode !== 200) {
response.resume();
reject(new Error(`Nasiko registry returned HTTP ${response.statusCode}.`));
return;
}
const chunks = [];
let totalBytes = 0;
response.on('data', chunk => {
totalBytes += chunk.length;
if (totalBytes > maxBytes) {
request.destroy(new Error('Nasiko registry response exceeded the size limit.'));
return;
}
chunks.push(chunk);
});
response.on('end', () => resolve(Buffer.concat(chunks)));
response.on('error', reject);
});
request.setTimeout(timeoutMs, () => request.destroy(new Error('Nasiko registry request timed out.')));
request.on('error', reject);
});
}
function inspectArchive(archivePath) {
const result = spawnSync('tar', ['-tvzf', archivePath], {
encoding: 'utf8',
shell: false,
timeout: 15000,
});
if (result.status !== 0) {
throw new Error('Nasiko archive inspection failed.');
}
return result.stdout.split(/\r?\n/).filter(Boolean).map(line => {
const typeMarker = line[0];
const entryPath = line.trim().split(/\s+/).at(-1);
return {
path: entryPath,
type: typeMarker === '-' ? 'file' : typeMarker === 'l' ? 'symlink' : 'other',
};
});
}
function extractArchive(archivePath, destination) {
const result = spawnSync('tar', ['-xzf', archivePath, '-C', destination], {
encoding: 'utf8',
shell: false,
timeout: 30000,
});
if (result.status !== 0) {
throw new Error('Nasiko archive extraction failed.');
}
}
function runVersion(executable) {
return spawnSync(executable, ['--version'], {
encoding: 'utf8',
shell: false,
timeout: 10000,
});
}
function defaultInstallDirectory(normalized, environment = process.env, homeDirectory = os.homedir()) {
if (normalized.os === 'windows') {
if (!environment.LOCALAPPDATA) throw new Error('LOCALAPPDATA is required on Windows.');
return path.join(environment.LOCALAPPDATA, 'nasiko', 'bin');
}
return path.join(homeDirectory, '.local', 'bin');
}
function validateInstallDirectory(installDirectory) {
if (typeof installDirectory !== 'string' || installDirectory.includes('\0') || !path.isAbsolute(installDirectory)) {
throw new Error('Nasiko install directory must be an absolute path.');
}
const resolved = path.resolve(installDirectory);
if (resolved === path.parse(resolved).root) {
throw new Error('Nasiko cannot install directly into a filesystem root.');
}
return resolved;
}
function assertDirectoryNotSymlink(directoryPath) {
if (!fs.existsSync(directoryPath)) return;
const stats = fs.lstatSync(directoryPath);
if (!stats.isDirectory() || stats.isSymbolicLink()) {
throw new Error('Nasiko install directory must be a real directory, not a symlink.');
}
}
async function installNasiko(options = {}, dependencies = {}) {
const version = options.version || 'v0.1.0';
const qualified = getQualifiedRelease(
version,
dependencies.platform || process.platform,
dependencies.arch || process.arch
);
const release = dependencies.releaseOverride
? { ...qualified, ...dependencies.releaseOverride }
: qualified;
const installDirectory = validateInstallDirectory(options.installDir || defaultInstallDirectory(
release,
dependencies.environment || process.env,
dependencies.homeDirectory || os.homedir()
));
const destination = path.join(installDirectory, release.binaryName);
const plan = {
dryRun: Boolean(options.dryRun),
version,
platform: release.os,
architecture: release.arch,
manifestDigest: release.manifestDigest,
registryOrigin: REGISTRY_ORIGIN,
destination,
};
if (options.dryRun) return plan;
if (!options.yes) throw new Error('Nasiko installation requires explicit --yes consent.');
assertDirectoryNotSymlink(installDirectory);
fs.mkdirSync(installDirectory, { recursive: true, mode: 0o755 });
assertDirectoryNotSymlink(installDirectory);
if (fs.existsSync(destination)) {
if (fs.lstatSync(destination).isSymbolicLink()) {
throw new Error('Refusing to replace a symlinked Nasiko executable.');
}
const existing = (dependencies.runVersion || runVersion)(destination);
const output = `${existing.stdout || ''}\n${existing.stderr || ''}`;
if (existing.status === 0 && output.includes(version)) {
return { ...plan, dryRun: false, installed: true, reused: true };
}
throw new Error('An incompatible Nasiko executable already exists at the destination.');
}
const retrieve = dependencies.fetchBytes || fetchBytes;
const manifestUrl = `${REGISTRY_ORIGIN}/v2/${REPOSITORY}/manifests/${release.manifestDigest}`;
const manifestBytes = await retrieve(manifestUrl, {
accept: 'application/vnd.oci.image.manifest.v1+json',
maxBytes: MAX_MANIFEST_BYTES,
});
assertDigest(manifestBytes, release.manifestDigest, 'Nasiko manifest');
const layer = validateManifest(manifestBytes);
const archiveUrl = `${REGISTRY_ORIGIN}/v2/${REPOSITORY}/blobs/${layer.digest}`;
const archiveBytes = await retrieve(archiveUrl, { maxBytes: MAX_ARCHIVE_BYTES });
if (archiveBytes.length !== layer.size) throw new Error('Nasiko archive size mismatch.');
assertDigest(archiveBytes, layer.digest, 'Nasiko archive');
const temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-nasiko-install-'));
try {
const archivePath = path.join(temporaryDirectory, 'nasiko.tar.gz');
const extractionDirectory = path.join(temporaryDirectory, 'extract');
fs.mkdirSync(extractionDirectory, { mode: 0o700 });
fs.writeFileSync(archivePath, archiveBytes, { mode: 0o600 });
const inspect = dependencies.inspectArchive || inspectArchive;
validateArchiveEntries(inspect(archivePath), release.binaryName);
(dependencies.extractArchive || extractArchive)(archivePath, extractionDirectory);
const extractedBinary = path.join(extractionDirectory, release.binaryName);
const extractedStats = fs.lstatSync(extractedBinary);
if (!extractedStats.isFile() || extractedStats.isSymbolicLink()) {
throw new Error('Extracted Nasiko binary is not a regular file.');
}
fs.chmodSync(extractedBinary, 0o755);
const stagedDestination = path.join(installDirectory, `.${release.binaryName}.tmp-${process.pid}`);
fs.copyFileSync(extractedBinary, stagedDestination, fs.constants.COPYFILE_EXCL);
fs.chmodSync(stagedDestination, 0o755);
fs.renameSync(stagedDestination, destination);
const versionResult = (dependencies.runVersion || runVersion)(destination);
const versionOutput = `${versionResult.stdout || ''}\n${versionResult.stderr || ''}`;
if (versionResult.status !== 0 || !versionOutput.includes(version)) {
fs.rmSync(destination, { force: true });
throw new Error('Installed Nasiko binary did not report the qualified version.');
}
const metadata = {
version,
platform: release.os,
architecture: release.arch,
manifestDigest: release.manifestDigest,
artifactDigest: layer.digest,
installedPath: destination,
};
const metadataPath = path.join(installDirectory, '.ecc-nasiko-install.json');
const temporaryMetadata = `${metadataPath}.tmp-${process.pid}`;
fs.writeFileSync(temporaryMetadata, `${JSON.stringify(metadata, null, 2)}\n`, { mode: 0o600 });
fs.renameSync(temporaryMetadata, metadataPath);
return { ...plan, dryRun: false, installed: true, reused: false, artifactDigest: layer.digest };
} finally {
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
}
}
module.exports = {
QUALIFIED_RELEASES,
REGISTRY_ORIGIN,
digestBytes,
fetchBytes,
getQualifiedRelease,
installNasiko,
normalizePlatform,
validateArchiveEntries,
validateInstallDirectory,
};
+134
View File
@@ -0,0 +1,134 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawnSync } = require('child_process');
const {
installNasiko,
normalizePlatform,
validateInstallDirectory,
} = require('./lib/nasiko-release');
function helpText() {
return `
ECC Nasiko control-plane bridge
Usage:
ecc nasiko status [--json]
ecc nasiko install --version v0.1.0 --yes [--install-dir <absolute-path>] [--json]
ecc nasiko install --version v0.1.0 --dry-run [--install-dir <absolute-path>] [--json]
The installer is opt-in, accepts only ECC-qualified pinned releases, downloads
content-addressed OCI artifacts from registry.nasiko.dev, verifies SHA-256
digests before extraction, and never executes fetched shell or PowerShell code.
`;
}
function parseInstallArguments(argumentsList) {
let options = { dryRun: false, installDir: undefined, json: false, version: undefined, yes: false };
for (let index = 0; index < argumentsList.length; index += 1) {
const argument = argumentsList[index];
if (argument === '--version' || argument === '--install-dir') {
const value = argumentsList[index + 1];
if (!value || value.startsWith('--')) throw new Error(`Missing value for ${argument}.`);
options = {
...options,
[argument === '--version' ? 'version' : 'installDir']: value,
};
index += 1;
} else if (argument === '--yes' || argument === '-y') {
options = { ...options, yes: true };
} else if (argument === '--dry-run') {
options = { ...options, dryRun: true };
} else if (argument === '--json') {
options = { ...options, json: true };
} else {
throw new Error(`Unknown Nasiko install argument: ${argument}`);
}
}
if (!options.version) throw new Error('Nasiko install requires --version v0.1.0.');
if (options.installDir) validateInstallDirectory(options.installDir);
return options;
}
function defaultExecutablePath() {
const normalized = normalizePlatform();
if (normalized.os === 'windows') {
return process.env.LOCALAPPDATA
? path.join(process.env.LOCALAPPDATA, 'nasiko', 'bin', normalized.binaryName)
: null;
}
return path.join(os.homedir(), '.local', 'bin', normalized.binaryName);
}
function resolveExecutable() {
const configured = process.env.ECC_NASIKO_CLI_EXECUTABLE;
const candidate = configured || defaultExecutablePath();
if (!candidate) return null;
if (!path.isAbsolute(candidate)) {
throw new Error('ECC_NASIKO_CLI_EXECUTABLE must be an absolute path.');
}
if (!fs.existsSync(candidate)) return null;
const stats = fs.lstatSync(candidate);
if (!stats.isFile() || stats.isSymbolicLink()) {
throw new Error('Nasiko executable must be a regular file, not a symlink.');
}
return candidate;
}
function readStatus() {
const executable = resolveExecutable();
if (!executable) return { installed: false, version: null, executable: null };
const result = spawnSync(executable, ['--version'], {
encoding: 'utf8',
shell: false,
timeout: 10000,
});
if (result.status !== 0) {
throw new Error('Nasiko executable failed its version check.');
}
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
const version = output.match(/\bv\d+\.\d+\.\d+\b/)?.[0] || null;
if (!version) throw new Error('Nasiko executable returned an unrecognized version.');
return { installed: true, version, executable };
}
async function main(argumentsList = process.argv.slice(2)) {
const [command, ...rest] = argumentsList;
if (!command || command === '--help' || command === '-h' || command === 'help') {
process.stdout.write(helpText());
return 0;
}
if (command === 'status') {
const unknown = rest.filter(argument => argument !== '--json');
if (unknown.length > 0) throw new Error(`Unknown Nasiko status argument: ${unknown[0]}`);
const status = readStatus();
if (rest.includes('--json')) process.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
else process.stdout.write(status.installed
? `Nasiko ${status.version} is installed at ${status.executable}.\n`
: 'Nasiko is not installed in the ECC-qualified location.\n');
return 0;
}
if (command === 'install') {
const options = parseInstallArguments(rest);
const result = await installNasiko(options);
if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
else if (result.dryRun) process.stdout.write(`Would install Nasiko ${result.version} to ${result.destination}.\n`);
else process.stdout.write(`${result.reused ? 'Using existing' : 'Installed'} Nasiko ${result.version} at ${result.destination}.\n`);
return 0;
}
throw new Error(`Unsupported Nasiko command: ${command}`);
}
if (require.main === module) {
main().then(code => {
process.exitCode = code;
}).catch(error => {
process.stderr.write(`Error: ${String(error?.message || error).replace(/[\r\n]+/g, ' ')}\n`);
process.exitCode = 1;
});
}
module.exports = { main, parseInstallArguments, readStatus, resolveExecutable };
+44
View File
@@ -0,0 +1,44 @@
---
name: nasiko-control-plane
description: Install, detect, and operate the optional Nasiko agent control plane through ECC with pinned artifacts, explicit consent, and telemetry and secrets boundaries.
---
# Nasiko Control Plane
Use this skill when a user explicitly asks to install, inspect, or operate the
Nasiko control plane with ECC.
## Safety contract
- Begin with `ecc nasiko status --json`. Status is read-only.
- Installation always requires explicit user consent and `--yes`.
- Install only an ECC-qualified pinned version, currently `v0.1.0`.
- Preview first with `ecc nasiko install --version v0.1.0 --dry-run --json`.
- Install with `ecc nasiko install --version v0.1.0 --yes --json` only after the
user reviews the version, registry origin, digest, and destination.
- Never replace the qualified command with a downloaded shell or PowerShell
bootstrap script.
- Never put secrets or credentials in command arguments, logs, skill output,
install metadata, or ECC state.
- Nasiko telemetry and any sharing with Nasiko or Ito must be opt-in and
separately disclosed. Installation is not telemetry consent.
## Lifecycle boundary
The initial ECC bridge supports only qualified installation and read-only
status. Use the canonical Nasiko CLI directly for connection, authentication,
launch, deployment, or shutdown until those verbs have their own verified ECC
contracts. Do not guess CLI verbs.
Installing the CLI does not prove that a control-plane server is running, an
agent is governed, routing or ACLs work, observability is complete, telemetry
was enabled, or Ito compute is connected. Report each state separately.
## Failure behavior
- If the platform, architecture, version, manifest, digest, archive, binary, or
destination fails validation, stop without executing the artifact.
- Do not fall back to `latest`.
- Do not search arbitrary `PATH` entries. Use ECC's qualified location or an
explicit absolute `ECC_NASIKO_CLI_EXECUTABLE` for development verification.
- Do not treat a partial or ambiguous installation as success.
@@ -0,0 +1,4 @@
interface:
display_name: "Nasiko Control Plane"
short_description: "Safely install and inspect the optional Nasiko control plane"
default_prompt: "Use $nasiko-control-plane to inspect or explicitly install the pinned Nasiko CLI without enabling telemetry or exposing secrets."