Files
Elydora-Node-SDK/README.md
T

308 lines
20 KiB
Markdown

# Elydora Node SDK
Official Node.js/TypeScript SDK for the [Elydora](https://elydora.com) tamper-evident audit platform. Build cryptographically verifiable audit trails for AI agent operations.
## Installation
```bash
npm install @elydora/sdk
```
Requires Node.js 18+ (uses built-in `crypto` module with Ed25519 support).
## Quick Start
```typescript
import { ElydoraClient } from '@elydora/sdk';
// Authenticate
const auth = await ElydoraClient.login('https://api.elydora.com', 'user@example.com', 'password');
// Create client
const client = new ElydoraClient({
orgId: auth.user.org_id,
agentId: 'my-agent-id',
privateKey: '<base64url-encoded-32-byte-ed25519-seed>',
baseUrl: 'https://api.elydora.com',
});
client.setToken(auth.token);
// Create and submit an operation
const eor = client.createOperation({
operationType: 'data.access',
subject: { user_id: 'u-123', resource: 'patient-record' },
action: { type: 'read', scope: 'full' },
payload: { record_id: 'rec-456' },
});
const { receipt } = await client.submitOperation(eor);
console.log('Operation submitted:', receipt.operation_id);
```
## CLI
The SDK includes a CLI for installing audit hooks into AI coding agents.
```bash
npx elydora install \
--agent claudecode \
--org_id org-123 \
--agent_id agent-456 \
--kid agent-456-key-v1
```
The CLI reads the private key and optional API token through hidden terminal prompts. For non-interactive installation, store each secret in an owner-only file and pass `--private_key_file <path>` and `--token_file <path>`. Secret values are rejected as command-line arguments because process listings and shell history can expose them. Agent IDs map to one physical directory directly under `~/.elydora`; portable filename rules and physical-directory checks apply before writes or recursive removal. Ambiguous uninstall discovery requires an explicit agent ID.
### Commands
| Command | Description |
|---------|-------------|
| `elydora install` | Install Elydora audit hook for a coding agent |
| `elydora uninstall` | Remove Elydora audit hook for a coding agent |
| `elydora status` | Show installation status for all agents |
| `elydora agents` | List supported coding agents |
### Supported Agents
| Agent | Key |
|-------|-----|
| Augment Code CLI | `augment` |
| Claude Code | `claudecode` |
| Codex CLI | `codex` |
| Cline | `cline` |
| Factory Droid | `droid` |
| Kimi Code | `kimi` |
| Grok Build | `grok` |
| Qwen Code | `qwen` |
| Cursor CLI | `cursor` |
| Gemini CLI | `gemini` |
| Kiro CLI | `kirocli` |
| Kiro IDE | `kiroide` |
| OpenCode | `opencode` |
| GitHub Copilot CLI | `copilot` |
| Letta Code | `letta` |
Claude Code installation writes exact matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups to `$CLAUDE_CONFIG_DIR/settings.json`, with `~/.claude/settings.json` selected when the variable is absent. Relative and empty overrides resolve from the current working directory, matching Claude Code's native path behavior. Each handler uses Claude Code's cross-platform exec form with the current Node.js executable, a single script argument, and a ten-second timeout. Managed runtimes preserve the complete native snake_case event payload and enforce frozen or revoked state through exit code `2`. Project, local, managed, plugin, skill, and agent hook sources remain unchanged and continue loading additively. User settings, generated runtimes, runtime config, and private key commit as one rollback-capable transaction. `disableAllHooks` blocks installation; managed hook policy and safe mode can suppress user hooks. Run `/hooks` and `claude doctor` after installation.
Codex installation writes exact `PreToolUse` and `PostToolUse` command groups to `$CODEX_HOME/hooks.json` (`~/.codex/hooks.json` by default) and preserves the complete native event payload. A configured `CODEX_HOME` follows Codex's existing-directory canonicalization rule. User TOML, project, plugin, and managed sources remain unchanged and continue loading additively. The hook file, generated runtimes, runtime config, and private key commit as one rollback-capable update. Run `/hooks` after installation and approve both Elydora definition hashes.
GitHub Copilot CLI installation writes exact `preToolUse`, `postToolUse`, and `postToolUseFailure` handlers to `$COPILOT_HOME/hooks/elydora-audit.json` (`~/.copilot/hooks/elydora-audit.json` by default). Empty home overrides select the default. Policy, repository, inline settings, cross-tool Claude, and plugin hook sources remain unchanged and continue loading additively. Elydora evaluates the effective `disableAllHooks` value across Copilot's documented settings precedence before writing. The managed hook file, exact legacy migration, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. Managed commands preserve the complete native camelCase payload and propagate freeze or revocation through exit code `2`; Copilot timeouts retain their documented fail-open behavior. Restart active Copilot CLI sessions after installation.
Cursor installation writes native `preToolUse`, `postToolUse`, and `postToolUseFailure` handlers to `~/.cursor/hooks.json` for both the CLI and IDE. Project and enterprise hook sources remain unchanged. The installation commits Cursor's hook file, generated runtimes, runtime config, and private key as one rollback-capable update. Managed commands use the current Node.js executable, preserve the complete Cursor JSON payload, return valid JSON, propagate freeze and revocation exit code `2`, and enable `failClosed` with a ten-second timeout.
Gemini CLI installation writes named, matchless `BeforeTool` and `AfterTool` command hooks to `$GEMINI_CLI_HOME/.gemini/settings.json` (`~/.gemini/settings.json` by default). Empty home overrides select the OS home; relative and literal-tilde values retain Gemini CLI's native path behavior. JSON comments and unrelated settings remain intact. Workspace, system-default, system-override, and extension hook sources remain unchanged and continue loading additively. User settings, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. Managed handlers preserve the complete native snake_case payload, emit valid JSON on successful execution, propagate freeze and revocation through exit code `2`, and use encoded PowerShell commands on Windows. `hooksConfig.enabled` and `hooksConfig.disabled` remain authoritative. Run `/hooks list` after installation to inspect effective hooks from every source.
Cline installation writes `PreToolUse.mjs` and `PostToolUse.mjs` to `$CLINE_DIR/hooks` (default `~/.cline/hooks`). Documents, `.clinerules/hooks`, and workspace `.cline/hooks` remain unchanged and continue loading additively. Both hook files, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. The wrappers preserve Cline 3's complete native payload, and the guard emits its documented one-line JSON cancellation control.
Factory Droid 0.175.0 installation resolves one active user hook source in this order: `~/.factory/hooks.json`, the legacy `~/.factory/hooks/hooks.json`, then the `hooks` field from user `settings.local.json` and `settings.json`. Current hook files use the documented top-level `hooks` container; legacy direct event maps remain readable until Factory migrates them. Elydora writes exact `PreToolUse` and `PostToolUse` groups to that source, removes only exact Elydora ownership from inactive user sources, and keeps project, folder, plugin, and organization hook definitions read-only. System, project, folder, and user `hooksDisabled` values follow Factory's extension-only precedence; system-managed `allowManagedHooksOnly` blocks a user-hook installation. Hook sources, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. Managed commands preserve the complete native snake_case payload, propagate frozen or revoked state through exit code `2`, and use Factory's native PowerShell call operator with explicit `$LASTEXITCODE` propagation on Windows. Run `/hooks` after installation to refresh Droid's session snapshot and verify remote organization policy.
Kimi installation writes exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` rules to every detected runtime: Kimi Code's `$KIMI_CODE_HOME/config.toml` (default `~/.kimi-code/config.toml`) and the legacy Python CLI's `~/.kimi/config.toml`. A fresh installation targets stable Kimi Code; an existing legacy home activates the migration contract. Empty `KIMI_CODE_HOME` values use the default. The selected TOML files, generated runtimes, runtime config, and private key commit as one rollback-capable transaction. Managed commands preserve Kimi's native snake_case event JSON, propagate freeze and revocation through exit code `2`, and use encoded PowerShell on Windows. Run `/hooks` to inspect the global rules.
Grok Build installation writes native global `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` hooks to `$GROK_HOME/hooks/elydora-audit.json`, with `~/.grok/hooks/elydora-audit.json` selected when `GROK_HOME` is absent. An empty `GROK_HOME` stops installation and status checks with guidance to unset the variable or provide an absolute home directory, preventing Grok Build 0.2.106 from resolving it to cwd-relative `hooks`. The hook file, generated runtimes, runtime config, and private key commit as one rollback-capable transaction. Managed commands preserve Grok's native camelCase JSON, return `{"decision":"deny","reason":"..."}` with exit code `2` for frozen or revoked agents, and use encoded PowerShell on Windows. Elydora rejects matcher-bearing `SessionStart`, `SessionEnd`, `Stop`, and `UserPromptSubmit` groups that the stable loader drops; other [xAI hook groups](https://docs.x.ai/build/features/hooks) retain matcher metadata and nullable handler environments. Project hooks follow Grok's `/hooks-trust` workflow; Elydora leaves project, plugin, `hooks-paths`, Claude Code, and Cursor compatibility sources unchanged. Run `grok inspect --json` and open `/hooks` to inspect the effective hook inventory.
Qwen Code 0.20.0 installation writes exact, named `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` command hooks to `$QWEN_HOME/settings.json` (default `~/.qwen/settings.json`). Explicit environment values lead Qwen's home bootstrap, followed by `<QWEN_HOME>/.env` or `~/.qwen/.env`, then `~/.env`; a discovered home contributes its own `.env`. System defaults, trusted workspace settings, system overrides, and trust rules remain read-only and participate in effective `disableAllHooks` evaluation. User settings, generated runtimes, runtime config, and private key commit through one rollback-capable transaction guarded by snapshots of every consulted source. Managed handlers use ten-second millisecond timeouts, preserve Qwen's complete native snake_case payload for success and failure events, and propagate frozen or revoked state through exit code `2`. Run `/hooks` after installation and restart active Qwen Code sessions.
Auggie installation commits `~/.augment/settings.json`, both platform wrappers, generated runtimes, runtime config, and private key as one rollback-capable transaction. The managed `PreToolUse` guard propagates exit code `2`; `PostToolUse` preserves Auggie's complete native hook payload. System, workspace, local workspace, and alternate `--augment-cache-dir` settings remain unchanged. Run `auggie tools list` to validate the effective user configuration.
Kiro IDE 1.0 installation is workspace-scoped. Run the installer from the workspace root; it writes `.kiro/hooks/elydora-audit.json` with exact `PreToolUse` and `PostToolUse` command hooks in Kiro's v1 JSON schema. The hook file, any exact Elydora legacy migration, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. Managed handlers preserve Kiro's complete native payload and propagate frozen or revoked state through exit code `2`. Open the Agent Hooks panel after installation and confirm both Elydora entries.
Kiro CLI installation commits the global v2 custom agent at `~/.kiro/agents/elydora-audit.json`, the CLI 2.13.0+ v3 global hooks at `~/.kiro/hooks/elydora-audit.json`, generated runtimes, runtime config, and private key through one rollback-capable transaction. The v2 agent uses exact lower-camel `preToolUse` and `postToolUse` handlers with ten-second millisecond timeouts. The v3 source uses Kiro's v1 schema with exact PascalCase `PreToolUse` and `PostToolUse` command hooks. Both contracts preserve the complete native event payload and propagate frozen or revoked state through exit code `2`. Validate v2 with `kiro-cli agent validate --path ~/.kiro/agents/elydora-audit.json` and start it with `kiro-cli --agent elydora-audit`; start the v3 TUI with `kiro-cli --v3`.
OpenCode 1.18.4 installation writes the named `ElydoraAuditPlugin` export to `${XDG_CONFIG_HOME:-~/.config}/opencode/plugins/elydora-audit.js`, matching the official global plugin loader's `.js` and `.ts` discovery contract. `XDG_CONFIG_HOME` must be absolute. The `tool.execute.before` hook synchronously evaluates the full native input and argument payload and throws on frozen or revoked state. The awaited `tool.execute.after` hook preserves the complete native input and output payload and reports audit runtime failures to stderr. The plugin, exact legacy `.mjs` migration, generated runtimes, runtime config, and private key commit through one rollback-capable transaction. Status requires physical paths, exact generated sources, the installed Node executable, canonical private keys, and matching runtime identity. Restart active OpenCode sessions after installation.
Letta Code 0.28.13 installation writes exact `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` groups to `${HOME:-~}/.letta/settings.json`, following the [official hook source and event contract](https://docs.letta.com/reference/deprecated/hooks). Workspace `.letta/settings.json` and `.letta/settings.local.json` remain read-only and participate in effective `hooks.disabled` evaluation. An explicit global `false` enables hooks; global `true`, project `true`, and project-local `true` disable them according to Letta's precedence. Managed command handlers use ten-second millisecond timeouts, the current absolute Node.js executable, PowerShell exit-code propagation on Windows, and complete native snake_case payloads. Frozen and revoked state propagates through exit code `2`; audit delivery failures remain visible through stderr, owner-only `error.log`, and exit code `1`, while Letta post events remain nonblocking. Global settings, generated runtimes, runtime config, and private key commit through one rollback-capable transaction guarded by snapshots of all three settings sources. Status requires an effective enabled state, exact hook and executable identity, physical files, canonical private keys, and matching runtime identity. Run `/hooks` and restart active Letta Code sessions after installation.
## API Reference
### Configuration
```typescript
const client = new ElydoraClient({
orgId: string, // Organization ID
agentId: string, // Agent ID
privateKey: string, // Base64url-encoded Ed25519 seed
baseUrl?: string, // API base URL (default: https://api.elydora.com)
ttlMs?: number, // Operation TTL in ms (default: 30000)
maxRetries?: number, // Max retries on transient failures (default: 3)
kid?: string, // Key ID (default: {agentId}-key-v1)
});
```
### Authentication
```typescript
// Register a new user and organization (returns session token)
const reg = await ElydoraClient.register(baseUrl, email, password, displayName?, orgName?);
// Login and receive a session token
const auth = await ElydoraClient.login(baseUrl, email, password);
// Set token on client
client.setToken(auth.token);
// Get current authenticated user profile
const { user } = await client.getMe();
// Issue a new API token (with optional TTL in seconds)
const { token, expires_at } = await client.issueApiToken(3600);
```
### Operations
```typescript
// Create a signed EOR locally (synchronous, no network call)
const eor = client.createOperation({
operationType: 'inference',
subject: { model: 'gpt-4' },
action: { type: 'completion' },
payload: { prompt: 'Hello' },
});
// Submit to API
const { receipt } = await client.submitOperation(eor);
// Retrieve an operation
const op = await client.getOperation(operationId);
// Verify integrity (signature, chain, receipt, merkle)
const result = await client.verifyOperation(operationId);
```
### Agent Management
```typescript
// Register a new agent
const agent = await client.registerAgent({
agent_id: 'my-agent',
integration_type: 'codex',
display_name: 'My Agent',
responsible_entity: 'team@example.com',
keys: [{ kid: 'key-v1', public_key: '<base64url>', algorithm: 'ed25519' }],
});
// Get agent details
const details = await client.getAgent(agentId);
// Freeze an agent
await client.freezeAgent(agentId, 'security review');
// Revoke a key
await client.revokeKey(agentId, kid, 'key rotation');
// List all agents in the organization
const { agents } = await client.listAgents();
// Unfreeze a previously frozen agent
await client.unfreezeAgent(agentId, 'review complete');
// Delete an agent permanently
const { deleted } = await client.deleteAgent(agentId);
```
### Audit
```typescript
const results = await client.queryAudit({
agent_id: 'agent-123',
operation_type: 'inference',
start_time: Date.now() - 86400000,
end_time: Date.now(),
limit: 50,
});
```
### Epochs
```typescript
const { epochs } = await client.listEpochs();
const { epoch } = await client.getEpoch(epochId);
```
### Exports
```typescript
const { export: exp } = await client.createExport({
start_time: Date.now() - 86400000,
end_time: Date.now(),
format: 'json',
});
const { exports } = await client.listExports();
const { export: detail, download_url } = await client.getExport(exportId);
// Download export file data
const data = await client.downloadExport(exportId);
```
### JWKS
```typescript
const { keys } = await client.getJWKS();
```
### Health
```typescript
// Check API health (no authentication required)
const health = await client.health();
// health.status, health.version, health.protocol_version, health.timestamp
```
### Client State
```typescript
// Get the current chain hash (useful for debugging/inspection)
const chainHash = client.getChainHash();
// Get the Ed25519 public key derived from the configured private key
const publicKey = client.getPublicKey();
```
### Crypto Utilities
The SDK exports low-level cryptographic primitives for advanced use:
```typescript
import {
jcsCanonicalise, // RFC 8785 JSON Canonicalization
sha256Base64url, // SHA-256 hash as base64url
computeChainHash, // Chain hash computation
computePayloadHash, // Payload hash computation
signEd25519, // Ed25519 signing
derivePublicKey, // Derive public key from private seed
ZERO_CHAIN_HASH, // Genesis chain hash constant
} from '@elydora/sdk';
```
### Utility Functions
```typescript
import {
uuidv7, // Generate a UUIDv7 (time-ordered, RFC 9562)
generateNonce, // Generate a 16-byte random nonce (base64url)
base64urlEncode, // Encode Buffer/Uint8Array to base64url (no padding)
base64urlDecode, // Decode base64url string to Buffer
} from '@elydora/sdk';
```
## Error Handling
```typescript
import { ElydoraError } from '@elydora/sdk';
try {
await client.submitOperation(eor);
} catch (err) {
if (err instanceof ElydoraError) {
console.error(err.code); // e.g. 'INVALID_SIGNATURE'
console.error(err.message); // Human-readable message
console.error(err.statusCode); // HTTP status code
console.error(err.requestId); // Request ID for support
}
}
```
## License
MIT