🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@open-agent-trust/cli

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@open-agent-trust/cli - npm Package Compare versions

Comparing version
1.0.7
to
1.0.8
+65
dist/cli/src/commands/compile.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = compile;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const path_2 = require("path");
const registry_artifacts_1 = require("../lib/registry-artifacts");
async function compile(options) {
try {
console.log('Compiling registry manifest...');
const registryDir = (0, path_1.join)(process.cwd(), '../registry');
const issuersDir = (0, path_1.join)(registryDir, 'issuers');
const files = await (0, promises_1.readdir)(issuersDir);
const entries = [];
for (const file of files) {
if (file.endsWith('.json')) {
const content = await (0, promises_1.readFile)((0, path_1.join)(issuersDir, file), 'utf8');
const parsed = JSON.parse(content);
entries.push(parsed);
}
}
entries.sort((a, b) => a.issuer_id.localeCompare(b.issuer_id));
const timestamp = new Date().toISOString();
const manifestExpiry = new Date(Date.now() + 60 * 60 * 1000).toISOString();
const revocationExpiry = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
const privateKeyPath = (0, path_2.resolve)(process.cwd(), options.privateKey);
const privateSeed = (await (0, promises_1.readFile)(privateKeyPath, 'utf8')).trim();
const rootKey = await (0, registry_artifacts_1.resolveActiveRootKey)(registryDir, privateSeed);
const unsignedManifest = {
schema_version: '1.0.0',
registry_id: 'open-trust-registry',
generated_at: timestamp,
expires_at: manifestExpiry,
entries
};
const manifest = {
...unsignedManifest,
signature: await (0, registry_artifacts_1.signRegistryArtifact)(unsignedManifest, privateSeed, rootKey.kid)
};
const revocationsPath = (0, path_1.join)(registryDir, 'revocations.json');
const currentRevocations = JSON.parse(await (0, promises_1.readFile)(revocationsPath, 'utf8'));
const unsignedRevocations = {
schema_version: currentRevocations.schema_version ?? '1.0.0',
generated_at: timestamp,
expires_at: revocationExpiry,
revoked_keys: currentRevocations.revoked_keys ?? [],
revoked_issuers: currentRevocations.revoked_issuers ?? []
};
const revocations = {
...unsignedRevocations,
signature: await (0, registry_artifacts_1.signRegistryArtifact)(unsignedRevocations, privateSeed, rootKey.kid)
};
const outputPath = (0, path_1.join)(registryDir, 'manifest.json');
await (0, promises_1.writeFile)(outputPath, JSON.stringify(manifest, null, 2));
await (0, promises_1.writeFile)(revocationsPath, JSON.stringify(revocations, null, 2));
console.log('✅ Success!');
console.log(`Signed Manifest saved to: ${outputPath}`);
console.log(`Signed Revocations saved to: ${revocationsPath}`);
console.log(`Total Issuers: ${manifest.entries.length}`);
}
catch (error) {
console.error('❌ Failed to compile registry:', error.message);
process.exit(1);
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.issue = void 0;
const jose_1 = require("jose");
const promises_1 = require("fs/promises");
const path_1 = require("path");
const crypto_1 = require("crypto");
const issue = async (options) => {
try {
console.log('[1/3] Loading Ed25519 private key...');
const keyPath = (0, path_1.resolve)(process.cwd(), options.privateKey);
const privateKeyB64 = await (0, promises_1.readFile)(keyPath, 'utf-8');
const privateKeyBytes = Buffer.from(privateKeyB64.trim(), 'base64url');
const seedHex = privateKeyBytes.subarray(0, 32).toString('hex');
console.log('[2/3] Preparing agent-attestation+jwt payload...');
const now = Math.floor(Date.now() / 1000);
const payload = {
sub: 'agent-instance-' + crypto.randomUUID().slice(0, 8),
aud: options.audience,
iat: now,
nonce: crypto.randomUUID(),
scope: options.scope.split(',').map(s => s.trim()),
constraints: {
time_bound: true
},
user_pseudonym: 'pairwise-' + crypto.randomUUID().slice(0, 8),
runtime_version: '1.0.0'
};
console.log('[3/3] Signing EdDSA attestation token...');
// Wrap the raw 32-byte Ed25519 seed in a PKCS#8 DER ASN.1 structure
// 302e020100300506032b657004220420 is the standard ASN.1 prefix for Ed25519 private keys
const pkcs8Der = Buffer.from('302e020100300506032b657004220420' + seedHex, 'hex');
const privateKeyObj = (0, crypto_1.createPrivateKey)({
key: pkcs8Der,
format: 'der',
type: 'pkcs8'
});
const jwt = await new jose_1.SignJWT(payload)
.setProtectedHeader({
alg: 'EdDSA',
kid: options.kid,
iss: options.issuerId,
typ: 'agent-attestation+jwt'
})
.setExpirationTime(now + options.expiresIn)
.sign(privateKeyObj);
console.log('\n✓ Test Attestation Generated Successfully:');
console.log('--------------------------------------------------');
console.log(jwt);
console.log('--------------------------------------------------');
console.log(`\nTo test verification, copy the string above and run:\nagent-trust verify <TOKEN> --audience ${options.audience}`);
}
catch (err) {
if (err instanceof Error) {
console.error('\n❌ Failed to generate test attestation:', err.message);
}
else {
console.error('\n❌ An unexpected error occurred while generating the test attestation.');
}
process.exit(1);
}
};
exports.issue = issue;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.keygen = void 0;
const ed = __importStar(require("@noble/ed25519"));
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// Polyfill for noble in raw node environments
if (!globalThis.crypto) {
globalThis.crypto = crypto.webcrypto;
}
const keygen = async (options) => {
try {
console.log(`\nGenerating Ed25519 keypair for issuer '${options.issuerId}'...\n`);
const privateKeyRaw = ed.utils.randomSecretKey();
const publicKeyRaw = await ed.getPublicKeyAsync(privateKeyRaw);
const privateKeyBase64Url = Buffer.from(privateKeyRaw).toString('base64url');
const publicKeyBase64Url = Buffer.from(publicKeyRaw).toString('base64url');
// Create the kid (Key ID) using a stable date prefix
const dateStr = new Date().toISOString().substring(0, 7); // e.g., 2026-03
const kid = `${options.issuerId}-${dateStr}`;
// Industry standard .pem extension with 'private' in filename for clarity
const privateKeyPath = path.join(options.outDir, `${options.issuerId}.private.pem`);
fs.writeFileSync(privateKeyPath, privateKeyBase64Url, { mode: 0o600 });
console.log(`✅ Keypair generated successfully!\n`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` Private Key: ${privateKeyPath}`);
console.log(` KID: ${kid}`);
console.log(` Algorithm: Ed25519`);
console.log(` Public Key: ${publicKeyBase64Url}`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
console.log(`⚠️ Keep your private key secret. Never commit it to a repo.\n`);
console.log(` To view it: cat ${privateKeyPath}`);
console.log(` To secure it: chmod 600 ${privateKeyPath}\n`);
console.log(` Note: Do not double-click the .pem file. On macOS, this opens`);
console.log(` Keychain Access. Always use 'cat' or a text editor from the terminal.\n`);
console.log(`Next steps:\n`);
console.log(` 1. Add your public key to your agent.json for Tier 3 identity:\n`);
console.log(` "identity": {`);
console.log(` "did": "did:web:yourdomain.com",`);
console.log(` "public_key": "${publicKeyBase64Url}"`);
console.log(` }\n`);
console.log(` 2. Host a DID document at https://yourdomain.com/.well-known/did.json`);
console.log(` (See docs: https://agentinternetruntime.com/spec/agent-json#becoming-tier-3)\n`);
console.log(` 3. To register as a trusted runtime issuer in the Trust Registry:`);
console.log(` npx @open-agent-trust/cli register --issuer-id ${options.issuerId} \\`);
console.log(` --display-name "Your Display Name" --website https://yourdomain.com \\`);
console.log(` --contact security@yourdomain.com --public-key ${publicKeyBase64Url}\n`);
}
catch (err) {
console.error('Failed to generate keypair:', err);
process.exit(1);
}
};
exports.keygen = keygen;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.prove = void 0;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
const PROOF_VERSION = 'oatr-proof-v1';
const ISSUER_ID_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
const prove = async (options) => {
try {
// Validate issuer_id format
if (!ISSUER_ID_PATTERN.test(options.issuerId)) {
throw new Error(`Invalid issuer_id "${options.issuerId}". Must be lowercase alphanumeric and hyphens only, ` +
`no leading/trailing hyphens. Example: my-runtime`);
}
console.log('[1/3] Loading Ed25519 private key...');
const keyPath = (0, path_1.resolve)(process.cwd(), options.privateKey);
const privateKeyB64 = await (0, promises_1.readFile)(keyPath, 'utf-8');
const privateKeyBuffer = Buffer.from(privateKeyB64.trim(), 'base64url');
if (privateKeyBuffer.length !== 32) {
throw new Error(`Invalid private key length (${privateKeyBuffer.length} bytes). ` +
`Must be 32 bytes (base64url encoded). Ensure this is an Ed25519 seed from 'agent-trust keygen'.`);
}
console.log('[2/3] Signing proof-of-key-ownership...');
const canonicalMessage = `${PROOF_VERSION}:${options.issuerId}`;
const messageBytes = Buffer.from(canonicalMessage, 'utf8');
const signatureBytes = await ed.signAsync(messageBytes, privateKeyBuffer);
const signature = Buffer.from(signatureBytes).toString('base64url');
const proofContent = [
'-----BEGIN OATR KEY OWNERSHIP PROOF-----',
`Canonical-Message: ${canonicalMessage}`,
`Signature: ${signature}`,
'-----END OATR KEY OWNERSHIP PROOF-----',
'' // trailing newline for POSIX compliance
].join('\n');
console.log('[3/3] Writing proof file...');
const outPath = options.outFile
? (0, path_1.resolve)(process.cwd(), options.outFile)
: (0, path_1.join)(process.cwd(), 'registry', 'proofs', `${options.issuerId}.proof`);
await (0, promises_1.mkdir)((0, path_1.dirname)(outPath), { recursive: true });
await (0, promises_1.writeFile)(outPath, proofContent);
console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` ✅ Proof of Key Ownership Generated`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` Issuer ID: ${options.issuerId}`);
console.log(` Proof file: ${outPath}`);
console.log(` Format: ${PROOF_VERSION}`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
console.log(`Next steps:\n`);
console.log(` 1. Ensure your issuer JSON is at registry/issuers/${options.issuerId}.json`);
console.log(` 2. Ensure your domain verification is live at:`);
console.log(` https://yourdomain.com/.well-known/agent-trust.json\n`);
console.log(` 3. Submit a Pull Request with both files:`);
console.log(` - registry/issuers/${options.issuerId}.json`);
console.log(` - registry/proofs/${options.issuerId}.proof\n`);
console.log(` The CI pipeline will verify your proof, check your domain,`);
console.log(` and auto-merge if all checks pass.\n`);
}
catch (error) {
console.error('❌ Failed to generate proof:', error.message);
process.exit(1);
}
};
exports.prove = prove;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.register = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const register = async (options) => {
const nowISO = new Date().toISOString();
// Hardcoded to standard generic capabilities for Phase 1 scaffolding.
// Real users would edit this draft JSON file.
let entry = {
"issuer_id": options.issuerId,
"display_name": options.displayName,
"website": options.website,
"security_contact": options.contact,
"status": "active",
"added_at": nowISO,
"last_verified": nowISO,
"public_keys": [
{
"kid": `${options.issuerId}-${nowISO.substring(0, 7)}`,
"algorithm": "Ed25519",
"public_key": options.publicKey,
"status": "active",
"issued_at": nowISO,
"expires_at": new Date(Date.now() + 31536000000).toISOString(), // +1 year
"deprecated_at": null,
"revoked_at": null
}
],
"capabilities": {
"supervision_model": "tiered",
"audit_logging": true,
"immutable_audit": false,
"attestation_format": "jwt",
"max_attestation_ttl_seconds": 3600,
"capabilities_verified": false
}
};
const outPath = options.outFile || path.join(process.cwd(), `${options.issuerId}.json`);
fs.writeFileSync(outPath, JSON.stringify(entry, null, 2));
console.log(`✅ Draft Issuer Entry generated at: ${outPath}`);
console.log(`\nPlease review the \"capabilities\" block to ensure it matches your runtime's exact profile.`);
console.log(`When ready, submit this file as a Pull Request to 'registry/issuers/' in the open source repository.`);
};
exports.register = register;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verify = void 0;
const src_1 = require("../../../sdk/typescript/src");
const verify = async (attestation, options) => {
try {
console.log(`[1/2] Fetching registry manifest from mirror (${options.mirror})...`);
const registry = await src_1.OpenAgentTrustRegistry.load(options.mirror);
console.log(`[2/2] Attempting 14-step verification against audience bounds (${options.audience})...`);
const result = await registry.verifyToken(attestation, options.audience);
if (result.valid) {
console.log(`\n✅ Valid Attestation`);
console.log(`Issuer: ${result.issuer?.display_name} (${result.issuer?.issuer_id})`);
console.log(`Token Subject: ${result.claims?.sub}`);
console.log(`Authorized As: ${result.claims?.user_pseudonym}`);
console.log(`Expires: ${new Date(result.claims?.exp * 1000).toISOString()}`);
console.log(`Constraints: `, result.claims?.constraints);
process.exit(0);
}
else {
console.error(`\n❌ Token mathematically rejected by the Registry.`);
console.error(`Reason: ${result.reason}`);
if (result.issuer) {
console.error(`Identified Issuer: ${result.issuer.display_name}`);
}
process.exit(1);
}
}
catch (err) {
if (err instanceof src_1.OpenAgentTrustRegistryError) {
console.error(`\n❌ Registry state rejected: ${err.code}`);
console.error(err.message);
process.exit(1);
}
console.error(`\n❌ Verification engine failure: ${err.message}`);
process.exit(1);
}
};
exports.verify = verify;
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const keygen_1 = require("./commands/keygen");
const register_1 = require("./commands/register");
const verify_1 = require("./commands/verify");
const issue_1 = require("./commands/issue");
const compile_1 = require("./commands/compile");
const prove_1 = require("./commands/prove");
const program = new commander_1.Command();
program
.name('agent-trust')
.description('Open Agent Trust Registry CLI Utilities')
.version('1.0.0');
program
.command('keygen')
.description('Generate a new Ed25519 keypair for your runtime')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.option('-o, --out-dir <path>', 'Directory to save the private key', './')
.action(keygen_1.keygen);
program
.command('register')
.description('Generate a compliant issuer_entry JSON file')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-n, --display-name <string>', 'Human-readable name of the runtime')
.requiredOption('-w, --website <url>', 'Public resolving website of the operator')
.requiredOption('-c, --contact <email>', 'Security responsible contact email')
.requiredOption('-k, --public-key <base64url>', 'The base64url Ed25519 public key generated from keygen')
.option('-o, --out-file <path>', 'Output path for the generated JSON')
.action(register_1.register);
program
.command('verify')
.description('Verify an agent attestation JWS against the registry')
.argument('<attestation>', 'The raw JWT/JWS token string')
.requiredOption('-a, --audience <url>', 'The origin URL of the service expecting the token')
.option('-m, --mirror <url>', 'Custom registry mirror URL', 'http://localhost:3000')
.action(verify_1.verify);
program
.command('issue')
.description('Generate a signed test agent-attestation+jwt token (useful for local integration testing)')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-k, --kid <string>', 'The key ID (kid) of the key used to sign')
.requiredOption('-p, --private-key <path>', 'Path to your .key file generated by keygen')
.requiredOption('-a, --audience <url>', 'The origin URL of the service expecting the token')
.option('-s, --scope <string>', 'Comma-separated list of scopes (e.g., read:email,send:email)', 'read:data')
.option('-e, --expires-in <seconds>', 'Expiration time in seconds', (val) => parseInt(val, 10), 3600)
.action(issue_1.issue);
program
.command('compile')
.description('Compiles and signs manifest.json and revocations.json from the registry folder')
.requiredOption('-p, --private-key <path>', 'Path to the Ed25519 root private key seed file')
.action(compile_1.compile);
program
.command('prove')
.description('Generate a cryptographic proof-of-key-ownership for registry registration')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-p, --private-key <path>', 'Path to your .private.pem key file')
.option('-o, --out-file <path>', 'Output path for the proof file')
.action(prove_1.prove);
program.parse(process.argv);
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.canonicalizeJson = canonicalizeJson;
exports.createPrivateKeyFromSeed = createPrivateKeyFromSeed;
exports.loadRootKeySet = loadRootKeySet;
exports.resolveActiveRootKey = resolveActiveRootKey;
exports.signRegistryArtifact = signRegistryArtifact;
const promises_1 = require("fs/promises");
const crypto_1 = require("crypto");
const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
function canonicalizeJson(value) {
if (value === null)
return 'null';
switch (typeof value) {
case 'boolean':
return value ? 'true' : 'false';
case 'number':
if (!Number.isFinite(value)) {
throw new Error('Canonical JSON does not support non-finite numbers.');
}
return JSON.stringify(value);
case 'string':
return JSON.stringify(value);
case 'object':
if (Array.isArray(value)) {
return `[${value.map((item) => canonicalizeJson(item)).join(',')}]`;
}
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalizeJson(value[key])}`)
.join(',')}}`;
default:
throw new Error(`Unsupported value type for canonical JSON: ${typeof value}`);
}
}
function createPrivateKeyFromSeed(seedBase64Url) {
const privateKeyBytes = Buffer.from(seedBase64Url.trim(), 'base64url');
if (privateKeyBytes.length !== 32) {
throw new Error(`Invalid private key length (${privateKeyBytes.length} bytes). Must be 32 bytes.`);
}
const seedHex = privateKeyBytes.toString('hex');
const pkcs8Der = Buffer.from(`302e020100300506032b657004220420${seedHex}`, 'hex');
return (0, crypto_1.createPrivateKey)({
key: pkcs8Der,
format: 'der',
type: 'pkcs8'
});
}
async function loadRootKeySet(registryDir) {
const raw = await (0, promises_1.readFile)((0, path_1.join)(registryDir, 'root-keys.json'), 'utf8');
return JSON.parse(raw);
}
async function resolveActiveRootKey(registryDir, privateSeed) {
const publicKey = Buffer.from(await ed.getPublicKeyAsync(Buffer.from(privateSeed.trim(), 'base64url'))).toString('base64url');
const rootKeys = await loadRootKeySet(registryDir);
const rootKey = rootKeys.keys.find((entry) => entry.public_key === publicKey && entry.status === 'active');
if (!rootKey) {
throw new Error('The supplied root private key does not match any active key in registry/root-keys.json.');
}
return rootKey;
}
async function signRegistryArtifact(unsignedArtifact, privateSeed, signatureKid) {
const canonicalPayload = canonicalizeJson(unsignedArtifact);
const signatureBytes = await ed.signAsync(Buffer.from(canonicalPayload, 'utf8'), Buffer.from(privateSeed.trim(), 'base64url'));
return {
algorithm: 'Ed25519',
kid: signatureKid,
value: Buffer.from(signatureBytes).toString('base64url')
};
}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.canonicalizeJson = canonicalizeJson;
exports.createPrivateKeyFromSeed = createPrivateKeyFromSeed;
exports.loadRootKeySet = loadRootKeySet;
exports.resolveActiveRootKey = resolveActiveRootKey;
exports.signRegistryArtifact = signRegistryArtifact;
const promises_1 = require("fs/promises");
const crypto_1 = require("crypto");
const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
function canonicalizeJson(value) {
if (value === null)
return 'null';
switch (typeof value) {
case 'boolean':
return value ? 'true' : 'false';
case 'number':
if (!Number.isFinite(value)) {
throw new Error('Canonical JSON does not support non-finite numbers.');
}
return JSON.stringify(value);
case 'string':
return JSON.stringify(value);
case 'object':
if (Array.isArray(value)) {
return `[${value.map((item) => canonicalizeJson(item)).join(',')}]`;
}
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalizeJson(value[key])}`)
.join(',')}}`;
default:
throw new Error(`Unsupported value type for canonical JSON: ${typeof value}`);
}
}
function createPrivateKeyFromSeed(seedBase64Url) {
const privateKeyBytes = Buffer.from(seedBase64Url.trim(), 'base64url');
if (privateKeyBytes.length !== 32) {
throw new Error(`Invalid private key length (${privateKeyBytes.length} bytes). Must be 32 bytes.`);
}
const seedHex = privateKeyBytes.toString('hex');
const pkcs8Der = Buffer.from(`302e020100300506032b657004220420${seedHex}`, 'hex');
return (0, crypto_1.createPrivateKey)({
key: pkcs8Der,
format: 'der',
type: 'pkcs8'
});
}
async function loadRootKeySet(registryDir) {
const raw = await (0, promises_1.readFile)((0, path_1.join)(registryDir, 'root-keys.json'), 'utf8');
return JSON.parse(raw);
}
async function resolveActiveRootKey(registryDir, privateSeed) {
const publicKey = Buffer.from(await ed.getPublicKeyAsync(Buffer.from(privateSeed.trim(), 'base64url'))).toString('base64url');
const rootKeys = await loadRootKeySet(registryDir);
const rootKey = rootKeys.keys.find((entry) => entry.public_key === publicKey && entry.status === 'active');
if (!rootKey) {
throw new Error('The supplied root private key does not match any active key in registry/root-keys.json.');
}
return rootKey;
}
async function signRegistryArtifact(unsignedArtifact, privateSeed, signatureKid) {
const canonicalPayload = canonicalizeJson(unsignedArtifact);
const signatureBytes = await ed.signAsync(Buffer.from(canonicalPayload, 'utf8'), Buffer.from(privateSeed.trim(), 'base64url'));
return {
algorithm: 'Ed25519',
kid: signatureKid,
value: Buffer.from(signatureBytes).toString('base64url')
};
}
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAgentTrustRegistry = void 0;
var registry_1 = require("./registry");
Object.defineProperty(exports, "OpenAgentTrustRegistry", { enumerable: true, get: function () { return registry_1.OpenAgentTrustRegistry; } });
__exportStar(require("./verify"), exports);
__exportStar(require("./registry-artifacts"), exports);
__exportStar(require("./types"), exports);
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAgentTrustRegistryError = void 0;
exports.loadBundledRootKeys = loadBundledRootKeys;
exports.verifyRegistryArtifacts = verifyRegistryArtifacts;
exports.canonicalizeRegistryArtifact = canonicalizeRegistryArtifact;
const fs_1 = require("fs");
const path_1 = require("path");
const crypto_1 = require("crypto");
class OpenAgentTrustRegistryError extends Error {
code;
constructor(code, message) {
super(message);
this.name = 'OpenAgentTrustRegistryError';
this.code = code;
}
}
exports.OpenAgentTrustRegistryError = OpenAgentTrustRegistryError;
function isRecord(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function assertString(value, label) {
if (typeof value !== 'string' || value.length === 0) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', `${label} must be a non-empty string`);
}
return value;
}
function parseDate(value, label) {
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', `${label} must be a valid ISO 8601 timestamp`);
}
return parsed;
}
function canonicalize(value) {
if (value === null)
return 'null';
switch (typeof value) {
case 'boolean':
return value ? 'true' : 'false';
case 'number':
if (!Number.isFinite(value)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'Canonical JSON does not support non-finite numbers');
}
return JSON.stringify(value);
case 'string':
return JSON.stringify(value);
case 'object':
if (Array.isArray(value)) {
return `[${value.map((item) => canonicalize(item)).join(',')}]`;
}
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalize(value[key])}`)
.join(',')}}`;
default:
throw new OpenAgentTrustRegistryError('malformed_registry_state', `Unsupported canonical JSON value type: ${typeof value}`);
}
}
function loadBundledJson(relativePath) {
const absolutePath = (0, path_1.resolve)(__dirname, '..', relativePath);
return JSON.parse((0, fs_1.readFileSync)(absolutePath, 'utf8'));
}
function loadBundledRootKeys() {
const rootKeys = loadBundledJson('root-keys.json');
if (!isRecord(rootKeys) || !Array.isArray(rootKeys.keys)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'Bundled root-keys.json is malformed');
}
return rootKeys;
}
function assertRegistrySignature(signature) {
if (!isRecord(signature)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'signature must be an object');
}
const algorithm = assertString(signature.algorithm, 'signature.algorithm');
if (algorithm !== 'Ed25519') {
throw new OpenAgentTrustRegistryError('invalid_registry_signature', `Unsupported registry signature algorithm: ${algorithm}`);
}
return {
algorithm: 'Ed25519',
kid: assertString(signature.kid, 'signature.kid'),
value: assertString(signature.value, 'signature.value')
};
}
function assertManifestLike(manifest) {
if (!isRecord(manifest)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'manifest must be an object');
}
assertString(manifest.schema_version, 'manifest.schema_version');
assertString(manifest.registry_id, 'manifest.registry_id');
assertString(manifest.generated_at, 'manifest.generated_at');
assertString(manifest.expires_at, 'manifest.expires_at');
if (!Array.isArray(manifest.entries)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'manifest.entries must be an array');
}
assertRegistrySignature(manifest.signature);
}
function assertRevocationListLike(revocations) {
if (!isRecord(revocations)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'revocations must be an object');
}
assertString(revocations.schema_version, 'revocations.schema_version');
assertString(revocations.generated_at, 'revocations.generated_at');
assertString(revocations.expires_at, 'revocations.expires_at');
if (!Array.isArray(revocations.revoked_keys) || !Array.isArray(revocations.revoked_issuers)) {
throw new OpenAgentTrustRegistryError('malformed_registry_state', 'revocation lists must be arrays');
}
assertRegistrySignature(revocations.signature);
}
function findTrustedRootKey(rootKeys, signature, now) {
const rootKey = rootKeys.keys.find((entry) => entry.kid === signature.kid);
if (!rootKey) {
throw new OpenAgentTrustRegistryError('unknown_root_key', `Unknown root key id: ${signature.kid}`);
}
if (rootKey.status !== 'active') {
throw new OpenAgentTrustRegistryError('invalid_registry_signature', `Root key ${signature.kid} is not active`);
}
const notBefore = parseDate(rootKey.not_before, `root key ${signature.kid} not_before`);
if (now < notBefore) {
throw new OpenAgentTrustRegistryError('invalid_registry_signature', `Root key ${signature.kid} is not valid yet`);
}
if (rootKey.not_after) {
const notAfter = parseDate(rootKey.not_after, `root key ${signature.kid} not_after`);
if (now > notAfter) {
throw new OpenAgentTrustRegistryError('invalid_registry_signature', `Root key ${signature.kid} has expired`);
}
}
return rootKey;
}
function verifySignedArtifact(artifact, kind, rootKeys, now) {
const signature = assertRegistrySignature(artifact.signature);
const trustedRootKey = findTrustedRootKey(rootKeys, signature, now);
const expiresAt = parseDate(artifact.expires_at, `${kind}.expires_at`);
if (now > expiresAt) {
throw new OpenAgentTrustRegistryError('stale_registry_state', `${kind} is expired as of ${artifact.expires_at}`);
}
const unsignedArtifact = { ...artifact, signature: undefined };
delete unsignedArtifact.signature;
const canonicalBytes = Buffer.from(canonicalize(unsignedArtifact), 'utf8');
const signatureBytes = Buffer.from(signature.value, 'base64url');
const publicKey = (0, crypto_1.createPublicKey)({
key: { kty: 'OKP', crv: 'Ed25519', x: trustedRootKey.public_key },
format: 'jwk'
});
const isValid = (0, crypto_1.verify)(null, canonicalBytes, publicKey, signatureBytes);
if (!isValid) {
throw new OpenAgentTrustRegistryError('invalid_registry_signature', `${kind} signature verification failed`);
}
}
function verifyRegistryArtifacts(manifest, revocations, options = {}) {
const now = options.now ?? new Date();
const rootKeys = options.rootKeys ?? loadBundledRootKeys();
assertManifestLike(manifest);
assertRevocationListLike(revocations);
verifySignedArtifact(manifest, 'manifest', rootKeys, now);
verifySignedArtifact(revocations, 'revocations', rootKeys, now);
return { manifest, revocations };
}
function canonicalizeRegistryArtifact(value) {
return canonicalize(value);
}
"use strict";
// sdk/typescript/src/registry.ts
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAgentTrustRegistry = void 0;
const verify_1 = require("./verify");
const registry_artifacts_1 = require("./registry-artifacts");
class OpenAgentTrustRegistry {
mirrorUrl;
manifest = null;
revocations = null;
lastFetchTime = 0;
// Refresh cache every 15 minutes by default
CACHE_TTL_MS = 15 * 60 * 1000;
constructor(mirrorUrl) {
this.mirrorUrl = mirrorUrl;
}
/**
* Initialize a new Registry Client and fetch the initial state.
*/
static async load(mirrorUrl) {
const registry = new OpenAgentTrustRegistry(mirrorUrl.replace(/\/$/, ''));
await registry.refresh();
return registry;
}
/**
* Manually trigger a refresh of the cached registry state from the network.
*/
async refresh() {
try {
const [manifestRes, revocationsRes] = await Promise.all([
fetch(`${this.mirrorUrl}/v1/registry`),
fetch(`${this.mirrorUrl}/v1/revocations`)
]);
if (!manifestRes.ok || !revocationsRes.ok) {
throw new registry_artifacts_1.OpenAgentTrustRegistryError('fetch_failed', `Failed to fetch registry state: ${manifestRes.status} / ${revocationsRes.status}`);
}
const manifest = await manifestRes.json();
const revocations = await revocationsRes.json();
const verifiedState = (0, registry_artifacts_1.verifyRegistryArtifacts)(manifest, revocations);
this.manifest = verifiedState.manifest;
this.revocations = verifiedState.revocations;
this.lastFetchTime = Date.now();
}
catch (err) {
console.error('[OpenAgentTrustRegistry] Failed to refresh state', err);
throw err;
}
}
/**
* Verify an incoming agent attestation JWS token locally against the loaded registry state.
*
* @param attestationJws The raw string JWS token
* @param expectedAudience The specific service origin (aud) expecting this token
* @param expectedNonce Optional nonce if the service requires intra-service replay protection
*/
async verifyToken(attestationJws, expectedAudience, expectedNonce) {
// Auto-refresh if cache is stale
if (Date.now() - this.lastFetchTime > this.CACHE_TTL_MS) {
await this.refresh();
}
if (!this.manifest || !this.revocations) {
throw new registry_artifacts_1.OpenAgentTrustRegistryError('registry_not_loaded', 'Registry state not loaded');
}
const verifiedState = (0, registry_artifacts_1.verifyRegistryArtifacts)(this.manifest, this.revocations);
this.manifest = verifiedState.manifest;
this.revocations = verifiedState.revocations;
return (0, verify_1.verifyAttestation)(attestationJws, this.manifest, this.revocations, expectedAudience, expectedNonce);
}
}
exports.OpenAgentTrustRegistry = OpenAgentTrustRegistry;
"use strict";
// sdk/typescript/src/types/attestation.ts
Object.defineProperty(exports, "__esModule", { value: true });
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
// sdk/typescript/src/types/index.ts
__exportStar(require("./registry"), exports);
__exportStar(require("./attestation"), exports);
"use strict";
// sdk/typescript/src/types/registry.ts
Object.defineProperty(exports, "__esModule", { value: true });
"use strict";
// sdk/typescript/src/verify.ts
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyAttestation = verifyAttestation;
const jose = __importStar(require("jose"));
/**
* Executes the 14-step Verification Protocol to assess an agent attestation.
* Operates purely locally in <1ms without any network calls.
*/
async function verifyAttestation(attestationJws, manifest, revocations, expectedAudience, expectedNonce) {
try {
// Step 1 & 2: Parse JWS and extract headers
const protectedHeader = jose.decodeProtectedHeader(attestationJws);
if (!protectedHeader.iss || !protectedHeader.kid || protectedHeader.alg !== 'EdDSA') {
return { valid: false, reason: 'invalid_signature' };
}
const issuerId = protectedHeader.iss;
const kid = protectedHeader.kid;
// Fast reject if issuer or key is explicitly on the 5-min revocation list
const isKeyRevoked = revocations.revoked_keys.some(k => k.kid === kid && k.issuer_id === issuerId);
const isIssuerRevoked = revocations.revoked_issuers.some(i => i.issuer_id === issuerId);
if (isKeyRevoked)
return { valid: false, reason: 'revoked_key' };
if (isIssuerRevoked)
return { valid: false, reason: 'revoked_issuer' };
// Step 3: Look up issuer
const issuer = manifest.entries.find(e => e.issuer_id === issuerId);
// Step 4: Unknown Issuer
if (!issuer)
return { valid: false, reason: 'unknown_issuer' };
// Step 5: Active check
if (issuer.status !== 'active')
return { valid: false, reason: 'revoked_issuer', issuer };
// Step 6: Locate key
const key = issuer.public_keys.find(k => k.kid === kid);
// Step 7: Unknown Key
if (!key)
return { valid: false, reason: 'unknown_key', issuer };
// Step 8: Revoked Key status check
if (key.status === 'revoked')
return { valid: false, reason: 'revoked_key', issuer };
// Step 9: Deprecated logic gracefully handled (we just accept it, perhaps logging later)
// Step 10: Check key expiration against current date
const now = new Date();
const keyExpiry = new Date(key.expires_at);
if (now > keyExpiry)
return { valid: false, reason: 'invalid_signature', issuer };
// Step 11 & 12: Cryptographically verify the signature
try {
// Convert base64url Ed25519 key to Uint8Array for noble/ed25519 or jose
// Jose handles the EdDSA verification inherently mapping base64 keys
const jwkDecoded = {
kty: 'OKP',
crv: 'Ed25519',
x: key.public_key // Base64url encoded
};
const importedKey = await jose.importJWK(jwkDecoded, 'EdDSA');
// This validates the signature and standard JWT claims (exp, etc.) concurrently
const { payload } = await jose.jwtVerify(attestationJws, importedKey, {
audience: expectedAudience,
algorithms: ['EdDSA']
});
// The payload is strictly typed as an agnostic claim map
const claims = payload;
// Step 13: Additional manual specific checks
if (expectedNonce && claims.nonce !== expectedNonce) {
return { valid: false, reason: 'nonce_mismatch', issuer };
}
// Must explicitly check aud since jwtVerify might be lenient depending on config
if (claims.aud !== expectedAudience) {
return { valid: false, reason: 'audience_mismatch', issuer };
}
// Step 14: All checks passed.
return {
valid: true,
issuer,
claims
};
}
catch (cryptoErr) {
// Includes JWT expired, invalid signature mathematically, or audience mismatch thrown by jose
if (cryptoErr instanceof jose.errors.JWTExpired) {
return { valid: false, reason: 'expired_attestation', issuer };
}
if (cryptoErr instanceof jose.errors.JWTClaimValidationFailed && cryptoErr.claim === 'aud') {
return { valid: false, reason: 'audience_mismatch', issuer };
}
return { valid: false, reason: 'invalid_signature', issuer };
}
}
catch (globalErr) {
// Catches malformed JWS tokens
return { valid: false, reason: 'invalid_signature' };
}
}
import { readFile } from 'fs/promises';
import { createPrivateKey } from 'crypto';
import { join } from 'path';
import * as ed from '@noble/ed25519';
interface RootKeyEntry {
kid: string;
algorithm: 'Ed25519';
public_key: string;
status: 'active' | 'retired';
not_before: string;
not_after: string | null;
}
interface RootKeySet {
schema_version: string;
registry_id: string;
generated_at: string;
keys: RootKeyEntry[];
}
export interface RegistrySignature {
algorithm: 'Ed25519';
kid: string;
value: string;
}
export function canonicalizeJson(value: unknown): string {
if (value === null) return 'null';
switch (typeof value) {
case 'boolean':
return value ? 'true' : 'false';
case 'number':
if (!Number.isFinite(value)) {
throw new Error('Canonical JSON does not support non-finite numbers.');
}
return JSON.stringify(value);
case 'string':
return JSON.stringify(value);
case 'object':
if (Array.isArray(value)) {
return `[${value.map((item) => canonicalizeJson(item)).join(',')}]`;
}
return `{${Object.keys(value as Record<string, unknown>)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalizeJson((value as Record<string, unknown>)[key])}`)
.join(',')}}`;
default:
throw new Error(`Unsupported value type for canonical JSON: ${typeof value}`);
}
}
export function createPrivateKeyFromSeed(seedBase64Url: string) {
const privateKeyBytes = Buffer.from(seedBase64Url.trim(), 'base64url');
if (privateKeyBytes.length !== 32) {
throw new Error(`Invalid private key length (${privateKeyBytes.length} bytes). Must be 32 bytes.`);
}
const seedHex = privateKeyBytes.toString('hex');
const pkcs8Der = Buffer.from(`302e020100300506032b657004220420${seedHex}`, 'hex');
return createPrivateKey({
key: pkcs8Der,
format: 'der',
type: 'pkcs8'
});
}
export async function loadRootKeySet(registryDir: string): Promise<RootKeySet> {
const raw = await readFile(join(registryDir, 'root-keys.json'), 'utf8');
return JSON.parse(raw) as RootKeySet;
}
export async function resolveActiveRootKey(registryDir: string, privateSeed: string): Promise<RootKeyEntry> {
const publicKey = Buffer.from(await ed.getPublicKeyAsync(Buffer.from(privateSeed.trim(), 'base64url'))).toString('base64url');
const rootKeys = await loadRootKeySet(registryDir);
const rootKey = rootKeys.keys.find((entry) => entry.public_key === publicKey && entry.status === 'active');
if (!rootKey) {
throw new Error('The supplied root private key does not match any active key in registry/root-keys.json.');
}
return rootKey;
}
export async function signRegistryArtifact(
unsignedArtifact: Record<string, unknown>,
privateSeed: string,
signatureKid: string
): Promise<RegistrySignature> {
const canonicalPayload = canonicalizeJson(unsignedArtifact);
const signatureBytes = await ed.signAsync(Buffer.from(canonicalPayload, 'utf8'), Buffer.from(privateSeed.trim(), 'base64url'));
return {
algorithm: 'Ed25519',
kid: signatureKid,
value: Buffer.from(signatureBytes).toString('base64url')
};
}
+34
-58
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });

@@ -39,3 +6,4 @@ exports.compile = compile;

const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
const path_2 = require("path");
const registry_artifacts_1 = require("../lib/registry-artifacts");
async function compile(options) {

@@ -47,3 +15,3 @@ try {

const files = await (0, promises_1.readdir)(issuersDir);
const issuers = {};
const entries = [];
for (const file of files) {

@@ -53,35 +21,43 @@ if (file.endsWith('.json')) {

const parsed = JSON.parse(content);
issuers[parsed.issuer_id] = parsed;
entries.push(parsed);
}
}
entries.sort((a, b) => a.issuer_id.localeCompare(b.issuer_id));
const timestamp = new Date().toISOString();
const expires = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // +1 hour
const manifestExpiry = new Date(Date.now() + 60 * 60 * 1000).toISOString();
const revocationExpiry = new Date(Date.now() + 5 * 60 * 1000).toISOString();
const privateKeyPath = (0, path_2.resolve)(process.cwd(), options.privateKey);
const privateSeed = (await (0, promises_1.readFile)(privateKeyPath, 'utf8')).trim();
const rootKey = await (0, registry_artifacts_1.resolveActiveRootKey)(registryDir, privateSeed);
const unsignedManifest = {
schema_version: '1.0.0',
registry_id: 'open-trust-registry',
generated_at: timestamp,
expires_at: manifestExpiry,
entries
};
const manifest = {
version: "1.0",
...unsignedManifest,
signature: await (0, registry_artifacts_1.signRegistryArtifact)(unsignedManifest, privateSeed, rootKey.kid)
};
const revocationsPath = (0, path_1.join)(registryDir, 'revocations.json');
const currentRevocations = JSON.parse(await (0, promises_1.readFile)(revocationsPath, 'utf8'));
const unsignedRevocations = {
schema_version: currentRevocations.schema_version ?? '1.0.0',
generated_at: timestamp,
expires_at: expires,
total_issuers: Object.keys(issuers).length,
issuers: issuers,
signature: ""
expires_at: revocationExpiry,
revoked_keys: currentRevocations.revoked_keys ?? [],
revoked_issuers: currentRevocations.revoked_issuers ?? []
};
const payloadToSign = JSON.stringify({
version: manifest.version,
generated_at: manifest.generated_at,
expires_at: manifest.expires_at,
total_issuers: manifest.total_issuers,
issuers: manifest.issuers
});
const privateKeyBuffer = Buffer.from(options.privateKey, 'base64url');
if (privateKeyBuffer.length !== 32) {
throw new Error("Invalid private key length. Must be 32 bytes (base64url encoded).");
}
// Use async signing to avoid needing the sync sha512 configuration
const signatureBytes = await ed.signAsync(Buffer.from(payloadToSign, 'utf8'), privateKeyBuffer);
const signatureStr = Buffer.from(signatureBytes).toString('base64url');
manifest.signature = `ed25519:${signatureStr}`;
const revocations = {
...unsignedRevocations,
signature: await (0, registry_artifacts_1.signRegistryArtifact)(unsignedRevocations, privateSeed, rootKey.kid)
};
const outputPath = (0, path_1.join)(registryDir, 'manifest.json');
await (0, promises_1.writeFile)(outputPath, JSON.stringify(manifest, null, 2));
await (0, promises_1.writeFile)(revocationsPath, JSON.stringify(revocations, null, 2));
console.log('✅ Success!');
console.log(`Signed Manifest saved to: ${outputPath}`);
console.log(`Total Issuers: ${manifest.total_issuers}`);
console.log(`Signed Revocations saved to: ${revocationsPath}`);
console.log(`Total Issuers: ${manifest.entries.length}`);
}

@@ -88,0 +64,0 @@ catch (error) {

@@ -30,2 +30,7 @@ "use strict";

catch (err) {
if (err instanceof registry_1.OpenAgentTrustRegistryError) {
console.error(`\n❌ Registry state rejected: ${err.code}`);
console.error(err.message);
process.exit(1);
}
console.error(`\n❌ Verification engine failure: ${err.message}`);

@@ -32,0 +37,0 @@ process.exit(1);

@@ -51,4 +51,4 @@ #!/usr/bin/env node

.command('compile')
.description('Compiles and signs the final manifest.json from the registry folder')
.requiredOption('-p, --private-key <key>', 'Ed25519 private key to sign the manifest')
.description('Compiles and signs manifest.json and revocations.json from the registry folder')
.requiredOption('-p, --private-key <path>', 'Path to the Ed25519 root private key seed file')
.action(compile_1.compile);

@@ -55,0 +55,0 @@ program

{
"name": "@open-agent-trust/cli",
"version": "1.0.7",
"version": "1.0.8",
"description": "CLI utilities for the Open Agent Trust Registry",

@@ -8,3 +8,3 @@ "license": "MIT",

"type": "git",
"url": "https://github.com/open-agent-trust/open-agent-trust-registry.git"
"url": "https://github.com/FransDevelopment/open-agent-trust-registry.git"
},

@@ -11,0 +11,0 @@ "author": "Open Agent Trust Registry Contributors",

import { readdir, readFile, writeFile } from 'fs/promises';
import { join } from 'path';
import * as ed from '@noble/ed25519';
import { resolve } from 'path';
import { resolveActiveRootKey, signRegistryArtifact } from '../lib/registry-artifacts';

@@ -12,5 +13,4 @@ export async function compile(options: { privateKey: string }) {

const files = await readdir(issuersDir);
const entries: Record<string, any>[] = [];
const issuers: Record<string, any> = {};
for (const file of files) {

@@ -20,43 +20,51 @@ if (file.endsWith('.json')) {

const parsed = JSON.parse(content);
issuers[parsed.issuer_id] = parsed;
entries.push(parsed);
}
}
entries.sort((a, b) => a.issuer_id.localeCompare(b.issuer_id));
const timestamp = new Date().toISOString();
const expires = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // +1 hour
const manifest = {
version: "1.0",
const manifestExpiry = new Date(Date.now() + 60 * 60 * 1000).toISOString();
const revocationExpiry = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
const privateKeyPath = resolve(process.cwd(), options.privateKey);
const privateSeed = (await readFile(privateKeyPath, 'utf8')).trim();
const rootKey = await resolveActiveRootKey(registryDir, privateSeed);
const unsignedManifest = {
schema_version: '1.0.0',
registry_id: 'open-trust-registry',
generated_at: timestamp,
expires_at: expires,
total_issuers: Object.keys(issuers).length,
issuers: issuers,
signature: ""
expires_at: manifestExpiry,
entries
};
const payloadToSign = JSON.stringify({
version: manifest.version,
generated_at: manifest.generated_at,
expires_at: manifest.expires_at,
total_issuers: manifest.total_issuers,
issuers: manifest.issuers
});
const manifest = {
...unsignedManifest,
signature: await signRegistryArtifact(unsignedManifest, privateSeed, rootKey.kid)
};
const privateKeyBuffer = Buffer.from(options.privateKey, 'base64url');
if (privateKeyBuffer.length !== 32) {
throw new Error("Invalid private key length. Must be 32 bytes (base64url encoded).");
}
const revocationsPath = join(registryDir, 'revocations.json');
const currentRevocations = JSON.parse(await readFile(revocationsPath, 'utf8'));
const unsignedRevocations = {
schema_version: currentRevocations.schema_version ?? '1.0.0',
generated_at: timestamp,
expires_at: revocationExpiry,
revoked_keys: currentRevocations.revoked_keys ?? [],
revoked_issuers: currentRevocations.revoked_issuers ?? []
};
const revocations = {
...unsignedRevocations,
signature: await signRegistryArtifact(unsignedRevocations, privateSeed, rootKey.kid)
};
// Use async signing to avoid needing the sync sha512 configuration
const signatureBytes = await ed.signAsync(Buffer.from(payloadToSign, 'utf8'), privateKeyBuffer);
const signatureStr = Buffer.from(signatureBytes).toString('base64url');
manifest.signature = `ed25519:${signatureStr}`;
const outputPath = join(registryDir, 'manifest.json');
await writeFile(outputPath, JSON.stringify(manifest, null, 2));
await writeFile(revocationsPath, JSON.stringify(revocations, null, 2));
console.log('✅ Success!');
console.log(`Signed Manifest saved to: ${outputPath}`);
console.log(`Total Issuers: ${manifest.total_issuers}`);
console.log(`Signed Revocations saved to: ${revocationsPath}`);
console.log(`Total Issuers: ${manifest.entries.length}`);

@@ -63,0 +71,0 @@ } catch (error: any) {

@@ -1,2 +0,2 @@

import { OpenAgentTrustRegistry } from '@open-agent-trust/registry';
import { OpenAgentTrustRegistry, OpenAgentTrustRegistryError } from '../../../sdk/typescript/src';

@@ -29,2 +29,7 @@ export const verify = async (attestation: string, options: { audience: string; mirror: string }) => {

} catch (err: any) {
if (err instanceof OpenAgentTrustRegistryError) {
console.error(`\n❌ Registry state rejected: ${err.code}`);
console.error(err.message);
process.exit(1);
}
console.error(`\n❌ Verification engine failure: ${err.message}`);

@@ -31,0 +36,0 @@ process.exit(1);

@@ -56,4 +56,4 @@ #!/usr/bin/env node

.command('compile')
.description('Compiles and signs the final manifest.json from the registry folder')
.requiredOption('-p, --private-key <key>', 'Ed25519 private key to sign the manifest')
.description('Compiles and signs manifest.json and revocations.json from the registry folder')
.requiredOption('-p, --private-key <path>', 'Path to the Ed25519 root private key seed file')
.action(compile);

@@ -60,0 +60,0 @@

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = compile;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
async function compile(options) {
try {
console.log('Compiling registry manifest...');
const registryDir = (0, path_1.join)(process.cwd(), '../registry');
const issuersDir = (0, path_1.join)(registryDir, 'issuers');
const files = await (0, promises_1.readdir)(issuersDir);
const issuers = {};
for (const file of files) {
if (file.endsWith('.json')) {
const content = await (0, promises_1.readFile)((0, path_1.join)(issuersDir, file), 'utf8');
const parsed = JSON.parse(content);
issuers[parsed.issuer_id] = parsed;
}
}
const timestamp = new Date().toISOString();
const expires = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // +1 hour
const manifest = {
version: "1.0",
generated_at: timestamp,
expires_at: expires,
total_issuers: Object.keys(issuers).length,
issuers: issuers,
signature: ""
};
const payloadToSign = JSON.stringify({
version: manifest.version,
generated_at: manifest.generated_at,
expires_at: manifest.expires_at,
total_issuers: manifest.total_issuers,
issuers: manifest.issuers
});
const privateKeyBuffer = Buffer.from(options.privateKey, 'base64url');
if (privateKeyBuffer.length !== 32) {
throw new Error("Invalid private key length. Must be 32 bytes (base64url encoded).");
}
// Use async signing to avoid needing the sync sha512 configuration
const signatureBytes = await ed.signAsync(Buffer.from(payloadToSign, 'utf8'), privateKeyBuffer);
const signatureStr = Buffer.from(signatureBytes).toString('base64url');
manifest.signature = `ed25519:${signatureStr}`;
const outputPath = (0, path_1.join)(registryDir, 'manifest.json');
await (0, promises_1.writeFile)(outputPath, JSON.stringify(manifest, null, 2));
console.log('✅ Success!');
console.log(`Signed Manifest saved to: ${outputPath}`);
console.log(`Total Issuers: ${manifest.total_issuers}`);
}
catch (error) {
console.error('❌ Failed to compile registry:', error.message);
process.exit(1);
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.issue = void 0;
const jose_1 = require("jose");
const promises_1 = require("fs/promises");
const path_1 = require("path");
const crypto_1 = require("crypto");
const issue = async (options) => {
try {
console.log('[1/3] Loading Ed25519 private key...');
const keyPath = (0, path_1.resolve)(process.cwd(), options.privateKey);
const privateKeyB64 = await (0, promises_1.readFile)(keyPath, 'utf-8');
const privateKeyBytes = Buffer.from(privateKeyB64.trim(), 'base64url');
const seedHex = privateKeyBytes.subarray(0, 32).toString('hex');
console.log('[2/3] Preparing agent-attestation+jwt payload...');
const now = Math.floor(Date.now() / 1000);
const payload = {
sub: 'agent-instance-' + crypto.randomUUID().slice(0, 8),
aud: options.audience,
iat: now,
nonce: crypto.randomUUID(),
scope: options.scope.split(',').map(s => s.trim()),
constraints: {
time_bound: true
},
user_pseudonym: 'pairwise-' + crypto.randomUUID().slice(0, 8),
runtime_version: '1.0.0'
};
console.log('[3/3] Signing EdDSA attestation token...');
// Wrap the raw 32-byte Ed25519 seed in a PKCS#8 DER ASN.1 structure
// 302e020100300506032b657004220420 is the standard ASN.1 prefix for Ed25519 private keys
const pkcs8Der = Buffer.from('302e020100300506032b657004220420' + seedHex, 'hex');
const privateKeyObj = (0, crypto_1.createPrivateKey)({
key: pkcs8Der,
format: 'der',
type: 'pkcs8'
});
const jwt = await new jose_1.SignJWT(payload)
.setProtectedHeader({
alg: 'EdDSA',
kid: options.kid,
iss: options.issuerId,
typ: 'agent-attestation+jwt'
})
.setExpirationTime(now + options.expiresIn)
.sign(privateKeyObj);
console.log('\n✓ Test Attestation Generated Successfully:');
console.log('--------------------------------------------------');
console.log(jwt);
console.log('--------------------------------------------------');
console.log(`\nTo test verification, copy the string above and run:\nagent-trust verify <TOKEN> --audience ${options.audience}`);
}
catch (err) {
if (err instanceof Error) {
console.error('\n❌ Failed to generate test attestation:', err.message);
}
else {
console.error('\n❌ An unexpected error occurred while generating the test attestation.');
}
process.exit(1);
}
};
exports.issue = issue;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.keygen = void 0;
const ed = __importStar(require("@noble/ed25519"));
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// Polyfill for noble in raw node environments
if (!globalThis.crypto) {
globalThis.crypto = crypto.webcrypto;
}
const keygen = async (options) => {
try {
console.log(`\nGenerating Ed25519 keypair for issuer '${options.issuerId}'...\n`);
const privateKeyRaw = ed.utils.randomSecretKey();
const publicKeyRaw = await ed.getPublicKeyAsync(privateKeyRaw);
const privateKeyBase64Url = Buffer.from(privateKeyRaw).toString('base64url');
const publicKeyBase64Url = Buffer.from(publicKeyRaw).toString('base64url');
// Create the kid (Key ID) using a stable date prefix
const dateStr = new Date().toISOString().substring(0, 7); // e.g., 2026-03
const kid = `${options.issuerId}-${dateStr}`;
// Industry standard .pem extension with 'private' in filename for clarity
const privateKeyPath = path.join(options.outDir, `${options.issuerId}.private.pem`);
fs.writeFileSync(privateKeyPath, privateKeyBase64Url, { mode: 0o600 });
console.log(`✅ Keypair generated successfully!\n`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` Private Key: ${privateKeyPath}`);
console.log(` KID: ${kid}`);
console.log(` Algorithm: Ed25519`);
console.log(` Public Key: ${publicKeyBase64Url}`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
console.log(`⚠️ Keep your private key secret. Never commit it to a repo.\n`);
console.log(` To view it: cat ${privateKeyPath}`);
console.log(` To secure it: chmod 600 ${privateKeyPath}\n`);
console.log(` Note: Do not double-click the .pem file. On macOS, this opens`);
console.log(` Keychain Access. Always use 'cat' or a text editor from the terminal.\n`);
console.log(`Next steps:\n`);
console.log(` 1. Add your public key to your agent.json for Tier 3 identity:\n`);
console.log(` "identity": {`);
console.log(` "did": "did:web:yourdomain.com",`);
console.log(` "public_key": "${publicKeyBase64Url}"`);
console.log(` }\n`);
console.log(` 2. Host a DID document at https://yourdomain.com/.well-known/did.json`);
console.log(` (See docs: https://agentinternetruntime.com/spec/agent-json#becoming-tier-3)\n`);
console.log(` 3. To register as a trusted runtime issuer in the Trust Registry:`);
console.log(` npx @open-agent-trust/cli register --issuer-id ${options.issuerId} \\`);
console.log(` --display-name "Your Display Name" --website https://yourdomain.com \\`);
console.log(` --contact security@yourdomain.com --public-key ${publicKeyBase64Url}\n`);
}
catch (err) {
console.error('Failed to generate keypair:', err);
process.exit(1);
}
};
exports.keygen = keygen;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.prove = void 0;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const ed = __importStar(require("@noble/ed25519"));
const PROOF_VERSION = 'oatr-proof-v1';
const ISSUER_ID_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
const prove = async (options) => {
try {
// Validate issuer_id format
if (!ISSUER_ID_PATTERN.test(options.issuerId)) {
throw new Error(`Invalid issuer_id "${options.issuerId}". Must be lowercase alphanumeric and hyphens only, ` +
`no leading/trailing hyphens. Example: my-runtime`);
}
console.log('[1/3] Loading Ed25519 private key...');
const keyPath = (0, path_1.resolve)(process.cwd(), options.privateKey);
const privateKeyB64 = await (0, promises_1.readFile)(keyPath, 'utf-8');
const privateKeyBuffer = Buffer.from(privateKeyB64.trim(), 'base64url');
if (privateKeyBuffer.length !== 32) {
throw new Error(`Invalid private key length (${privateKeyBuffer.length} bytes). ` +
`Must be 32 bytes (base64url encoded). Ensure this is an Ed25519 seed from 'agent-trust keygen'.`);
}
console.log('[2/3] Signing proof-of-key-ownership...');
const canonicalMessage = `${PROOF_VERSION}:${options.issuerId}`;
const messageBytes = Buffer.from(canonicalMessage, 'utf8');
const signatureBytes = await ed.signAsync(messageBytes, privateKeyBuffer);
const signature = Buffer.from(signatureBytes).toString('base64url');
const proofContent = [
'-----BEGIN OATR KEY OWNERSHIP PROOF-----',
`Canonical-Message: ${canonicalMessage}`,
`Signature: ${signature}`,
'-----END OATR KEY OWNERSHIP PROOF-----',
'' // trailing newline for POSIX compliance
].join('\n');
console.log('[3/3] Writing proof file...');
const outPath = options.outFile
? (0, path_1.resolve)(process.cwd(), options.outFile)
: (0, path_1.join)(process.cwd(), 'registry', 'proofs', `${options.issuerId}.proof`);
await (0, promises_1.mkdir)((0, path_1.dirname)(outPath), { recursive: true });
await (0, promises_1.writeFile)(outPath, proofContent);
console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` ✅ Proof of Key Ownership Generated`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
console.log(` Issuer ID: ${options.issuerId}`);
console.log(` Proof file: ${outPath}`);
console.log(` Format: ${PROOF_VERSION}`);
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
console.log(`Next steps:\n`);
console.log(` 1. Ensure your issuer JSON is at registry/issuers/${options.issuerId}.json`);
console.log(` 2. Ensure your domain verification is live at:`);
console.log(` https://yourdomain.com/.well-known/agent-trust.json\n`);
console.log(` 3. Submit a Pull Request with both files:`);
console.log(` - registry/issuers/${options.issuerId}.json`);
console.log(` - registry/proofs/${options.issuerId}.proof\n`);
console.log(` The CI pipeline will verify your proof, check your domain,`);
console.log(` and auto-merge if all checks pass.\n`);
}
catch (error) {
console.error('❌ Failed to generate proof:', error.message);
process.exit(1);
}
};
exports.prove = prove;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.register = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const register = async (options) => {
const nowISO = new Date().toISOString();
// Hardcoded to standard generic capabilities for Phase 1 scaffolding.
// Real users would edit this draft JSON file.
let entry = {
"issuer_id": options.issuerId,
"display_name": options.displayName,
"website": options.website,
"security_contact": options.contact,
"status": "active",
"added_at": nowISO,
"last_verified": nowISO,
"public_keys": [
{
"kid": `${options.issuerId}-${nowISO.substring(0, 7)}`,
"algorithm": "Ed25519",
"public_key": options.publicKey,
"status": "active",
"issued_at": nowISO,
"expires_at": new Date(Date.now() + 31536000000).toISOString(), // +1 year
"deprecated_at": null,
"revoked_at": null
}
],
"capabilities": {
"supervision_model": "tiered",
"audit_logging": true,
"immutable_audit": false,
"attestation_format": "jwt",
"max_attestation_ttl_seconds": 3600,
"capabilities_verified": false
}
};
const outPath = options.outFile || path.join(process.cwd(), `${options.issuerId}.json`);
fs.writeFileSync(outPath, JSON.stringify(entry, null, 2));
console.log(`✅ Draft Issuer Entry generated at: ${outPath}`);
console.log(`\nPlease review the \"capabilities\" block to ensure it matches your runtime's exact profile.`);
console.log(`When ready, submit this file as a Pull Request to 'registry/issuers/' in the open source repository.`);
};
exports.register = register;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verify = void 0;
const registry_1 = require("@open-agent-trust/registry");
const verify = async (attestation, options) => {
try {
console.log(`[1/2] Fetching registry manifest from mirror (${options.mirror})...`);
const registry = await registry_1.OpenAgentTrustRegistry.load(options.mirror);
console.log(`[2/2] Attempting 14-step verification against audience bounds (${options.audience})...`);
const result = await registry.verifyToken(attestation, options.audience);
if (result.valid) {
console.log(`\n✅ Valid Attestation`);
console.log(`Issuer: ${result.issuer?.display_name} (${result.issuer?.issuer_id})`);
console.log(`Token Subject: ${result.claims?.sub}`);
console.log(`Authorized As: ${result.claims?.user_pseudonym}`);
console.log(`Expires: ${new Date(result.claims?.exp * 1000).toISOString()}`);
console.log(`Constraints: `, result.claims?.constraints);
process.exit(0);
}
else {
console.error(`\n❌ Token mathematically rejected by the Registry.`);
console.error(`Reason: ${result.reason}`);
if (result.issuer) {
console.error(`Identified Issuer: ${result.issuer.display_name}`);
}
process.exit(1);
}
}
catch (err) {
console.error(`\n❌ Verification engine failure: ${err.message}`);
process.exit(1);
}
};
exports.verify = verify;
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const keygen_1 = require("./commands/keygen");
const register_1 = require("./commands/register");
const verify_1 = require("./commands/verify");
const issue_1 = require("./commands/issue");
const compile_1 = require("./commands/compile");
const prove_1 = require("./commands/prove");
const program = new commander_1.Command();
program
.name('agent-trust')
.description('Open Agent Trust Registry CLI Utilities')
.version('1.0.0');
program
.command('keygen')
.description('Generate a new Ed25519 keypair for your runtime')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.option('-o, --out-dir <path>', 'Directory to save the private key', './')
.action(keygen_1.keygen);
program
.command('register')
.description('Generate a compliant issuer_entry JSON file')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-n, --display-name <string>', 'Human-readable name of the runtime')
.requiredOption('-w, --website <url>', 'Public resolving website of the operator')
.requiredOption('-c, --contact <email>', 'Security responsible contact email')
.requiredOption('-k, --public-key <base64url>', 'The base64url Ed25519 public key generated from keygen')
.option('-o, --out-file <path>', 'Output path for the generated JSON')
.action(register_1.register);
program
.command('verify')
.description('Verify an agent attestation JWS against the registry')
.argument('<attestation>', 'The raw JWT/JWS token string')
.requiredOption('-a, --audience <url>', 'The origin URL of the service expecting the token')
.option('-m, --mirror <url>', 'Custom registry mirror URL', 'http://localhost:3000')
.action(verify_1.verify);
program
.command('issue')
.description('Generate a signed test agent-attestation+jwt token (useful for local integration testing)')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-k, --kid <string>', 'The key ID (kid) of the key used to sign')
.requiredOption('-p, --private-key <path>', 'Path to your .key file generated by keygen')
.requiredOption('-a, --audience <url>', 'The origin URL of the service expecting the token')
.option('-s, --scope <string>', 'Comma-separated list of scopes (e.g., read:email,send:email)', 'read:data')
.option('-e, --expires-in <seconds>', 'Expiration time in seconds', (val) => parseInt(val, 10), 3600)
.action(issue_1.issue);
program
.command('compile')
.description('Compiles and signs the final manifest.json from the registry folder')
.requiredOption('-p, --private-key <key>', 'Ed25519 private key to sign the manifest')
.action(compile_1.compile);
program
.command('prove')
.description('Generate a cryptographic proof-of-key-ownership for registry registration')
.requiredOption('-i, --issuer-id <string>', 'Your globally unique issuer identifier')
.requiredOption('-p, --private-key <path>', 'Path to your .private.pem key file')
.option('-o, --out-file <path>', 'Output path for the proof file')
.action(prove_1.prove);
program.parse(process.argv);