Node.js SDK
지원 모드: Hosted Hybrid Local 제공 플랜: Free Solo Teams
영어 원문 -- 번역은 기술 검토 대기 중입니다
The Control Zero Node.js SDK provides deterministic policy enforcement for AI agents running in JavaScript and TypeScript environments. A deny stops the guarded call before it executes. Hosted policies arrive as signed bundles that are verified before use, and the SDK fails closed by default when it cannot establish coverage.
설치
@controlzero 패키지는 npmjs.org가 아니라 Control Zero 레지스트리
(https://npm.controlzero.ai)에서 제공됩니다. .npmrc(프로젝트 또는
사용자 수준)에서 스코프가 이 레지스트리를 가리키도록 한 번만 설정하세요.
@controlzero:registry=https://npm.controlzero.ai
그런 다음 설치합니다.
npm install @controlzero/sdk
이 명령은 이 페이지에 문서화된 기능 집합과 일치하는 현재 1.13.x 버전 계열을
설치합니다. @controlzero 스코프 외부의 종속성은 계속 npmjs.org에서 가져옵니다.
요구 사항:
- Node.js 18 이상
- TypeScript 5.0+ (선택 사항, 타입 검사를 사용하는 경우)
빠른 시작
배포 모드를 선택하세요.
Python SDK와 달리, Node.js의 new Client({apiKey}) 생성자는 로컬 정책 없이
apiKey만 제공 하면 오류를 발생시킵니다. Hosted 모드에서는 비동기 팩토리를 사용하세요.
// Hosted mode -- MUST use async factory
const cz = await Client.create({ apiKey: 'cz_live_your_api_key_here' });
// Local mode only -- synchronous constructor works
const cz = new Client({ policyFile: 'controlzero.yaml' });
- Hosted
- Hybrid
- Local
Hosted 정책을 대시보드에서 가져옵니다.
영어 원문 -- 번역은 기술 검토 대기 중입니다
Audit in cloud.
대부분의 팀에 권장합니다.
import { Client } from '@controlzero/sdk';
// MUST use async factory for hosted mode
const client = await Client.create({ apiKey: 'cz_live_your_api_key_here' });
// Or use env var: export CONTROLZERO_API_KEY="cz_live_..."
영어 원문 -- 번역은 기술 검토 대기 중입니다
The SDK pulls the signed policy bundle from the dashboard at first call, verifies its signature, decrypts locally, and enforces every call against the dashboard policy. Audit ships to the remote trail automatically.
Hybrid 정책 파일은 사용자가 직접 관리합니다.
영어 원문 -- 번역은 기술 검토 대기 중입니다
API key enables remote audit only.
import { Client } from '@controlzero/sdk';
const client = await Client.create({
apiKey: 'cz_live_your_api_key_here',
policyFile: './controlzero.yaml',
});
controlzero.yaml이 적용을 통제합니다.
영어 원문 -- 번역은 기술 검토 대기 중입니다
The API key sends audit to your dashboard.
Local API 키가 없습니다.
영어 원문 -- 번역은 기술 검토 대기 중입니다
No network calls.
완전한 오프라인입니다. 에어갭 환경과 호환됩니다.
import { Client } from '@controlzero/sdk';
// From a file -- synchronous constructor works for local mode
const client = new Client({ policyFile: './controlzero.yaml' });
// Or inline policy
const client = new Client({
policy: {
rules: [
{ allow: 'database:query', reason: 'Reads are permitted' },
{ deny: 'database:execute', reason: 'Writes are blocked' },
],
},
});
영어 원문 -- 번역은 기술 검토 대기 중입니다
Every decision and its event-level coverage is written to ./controlzero.log, so “did not run” remains distinguishable from “ran and found nothing.”
구성
Hosted Hosted 모드(권장)
영어 원문 -- 번역은 기술 검토 대기 중입니다
Pass only your project API key via the async Client.create factory.
The SDK pulls the signed policy bundle from the dashboard at first
call, verifies its signature, decrypts locally, and enforces every
call against the dashboard policy. Audit ships to the remote trail
automatically.
import { Client } from '@controlzero/sdk';
const client = await Client.create({ apiKey: 'cz_live_your_api_key_here' });
new Client({ apiKey })(동기식)는 API 키만으로는 생성을 거부하고
Client.create()를 사용하도록 안내합니다. Hosted 부트스트랩은
I/O 호출이므로 await할 수 있어야 하기 때문입니다.
Local 로컬 정책 파일 사용
import { Client } from '@controlzero/sdk';
const client = new Client({ policyFile: './controlzero.yaml' });
인라인 정책
import { Client } from '@controlzero/sdk';
const client = new Client({
policy: {
rules: [
{ allow: 'database:query', reason: 'Reads are permitted' },
{ deny: 'database:*', reason: 'All other database operations are blocked' },
],
},
});
database:query, database:execute, database:delete는 레거시 액션 이름으로, 표준 클래스인 database:read, database:write, database:admin과 동일한 호출에 일치합니다. 새 정책에서는 표준 이름을 사용하는 것이 좋으며, 레거시 이름을 사용하는 기존 규칙은 변경 없이 계속 동작합니다.
와일드카드 리소스 매칭
resources: ['*']를 나열한 규칙은 호출자가 context.resource를 전달했는지와 관계없이 모든 호출에 일치합니다. 규칙을 모든 호출에 보편적으로 적용해야 하고 호출별 리소스가 없거나 전달하고 싶지 않을 때 사용하세요.
// Policy rule with universal resource matching
// { effect: 'allow', actions: ['database:read'], resources: ['*'] }
// This call matches even though context.resource is not set:
cz.guard('database', { method: 'query', args: { sql: 'SELECT 1' } });
와일드카드가 아닌 리소스 패턴(예: resources: ['table/orders'])은 규칙이 적용되려면 호출자가 일치하는 context.resource를 전달해야 합니다.
환경 변수
인수를 직접 전달하는 대신 환경 변수로 SDK를 구성할 수 있습니다.
export CONTROLZERO_POLICY_FILE="./controlzero.yaml"
import { Client } from '@controlzero/sdk';
// Reads CONTROLZERO_POLICY_FILE from environment, or auto-discovers
// controlzero.yaml, controlzero.yml, or controlzero.json in cwd
const client = new Client();
구성 옵션
| 옵 션 | 환경 변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|---|
apiKey | CONTROLZERO_API_KEY | string | - | Project API key. Use Client.create({ apiKey }) to enable hosted mode (auto-pulls the signed policy bundle from the dashboard; remote audit). |
policy | - | object | - | 규칙이 포함된 인라인 정책 객체. |
policyFile | CONTROLZERO_POLICY_FILE | string | - | YAML 또는 JSON 정책 파일 경로. |
strictHosted | - | bool | false | Hybrid(API 키 + 로컬 정책) 구성에서 경고하는 대신 오류를 발생시킵니다. |
logPath | - | string | ./controlzero.log | 로컬 감사 로그 파일 경로. |
logRotation | - | string | daily | 감사 로그 순환 주기. |
logRetention | - | string | 30 days | How long to keep rotated logs. |
logCompression | - | string | null | 순환된 로그 압축(예: gz). |
logFormat | - | string | json | 감사 로그 형식(json 또는 pretty). |
refreshIntervalSeconds | - | number | 300 | Hosted 모드: 대시보드에 새 정책 번들이 있는지 확인하는 주기. |
agentName | CZ_AGENT_NAME | string | - | 귀속을 위해 감사 이벤트에 기록되는 레이블. |
Node SDK의 기본 refreshIntervalSeconds는 300(5분)입니다.
Python SDK의 기본 refresh_interval_seconds는 60입니다.
두 폴링 주기를 맞추려면 refreshIntervalSeconds: 60으로 설정하세요.
정책 확인 순서: policy 옵션, policyFile 옵션, CONTROLZERO_POLICY_FILE 환경 변수, 그다음 cwd에서 자동 탐색된 첫 번째 파일(controlzero.yaml, 그다음 controlzero.yml, 그다음 controlzero.json -- 먼저 존재하는 파일이 우선), 그 이후에는 한 번 경고를 출력하고 아무 동작 없이 통과시킵니다. 정책 파일은 YAML 또는 JSON일 수 있으며, 둘 다 동일한 스키마를 사용합니다.
기본 사용법
도구 호출 평가
기본 메서드는 guard()이며, 로드된 정책에 대해 도구 호출을 평가합니다.
import { Client } from '@controlzero/sdk';
const client = new Client({ policyFile: './controlzero.yaml' });
const decision = client.guard('database', {
method: 'query',
args: { sql: 'SELECT * FROM orders' },
});
console.log(decision.effect); // "allow" or "deny"
console.log(decision.reason); // human-readable reason
console.log(decision.policyId); // matching policy ID
거부된 액션 처리
정책이 액션을 거부하면 결정의 effect를 확인하거나 PolicyDeniedError를 잡으세요.
import { Client, PolicyDeniedError } from '@controlzero/sdk';
const client = new Client({ policyFile: './controlzero.yaml' });
const decision = client.guard('filesystem', {
method: 'write_file',
args: { path: '/data/output.csv', content: '...' },
});
if (decision.effect === 'deny') {
console.log(`Blocked: ${decision.reason}`);
console.log(`Policy: ${decision.policyId}`);
}
또는 raiseOnDeny를 사용해 거부 시 예외를 던지게 할 수 있습니다.
try {
client.guard('filesystem', {
method: 'write_file',
args: { path: '/data/output.csv' },
raiseOnDeny: true,
});
} catch (error) {
if (error instanceof PolicyDeniedError) {
console.log(`Blocked: ${error.message}`);
}
}
API 레퍼런스
Client
기본 클라이언트 클래스입니다. guard()는 동기식입니다. 로컬 정책, Hybrid, 정책 없는
통과(pass-through) 클라이언트는 생성도 동기식입니다. API 키만 사용하는 Hosted
부트스트랩만 비동기 팩토리가 필요합니다.
new Client(options?)
새 Control Zero 클라이언트를 생성합니다. 사용 가능한 매개변수는 구성 옵션을
참조하세요. initialize() 호출은 필요하지 않으며, 생성자가 로컬 정책을 즉시 불러옵니다.
정책이 전혀 구성되지 않은 경우 클라이언트는 한 번 경고하고 호출을 그대로 통과시킵니다.
로컬 정책 없이 apiKey를 전달하면 HostedModeNotImplemented가 발생합니다.
Hosted 모드에는 Client.create()를 사용하세요.
static Client.create(options?): Promise<Client>
영어 원문 -- 번역은 기술 검토 대기 중입니다
Async factory. Required for hosted mode: it fetches the signed policy bundle, verifies its signature and decrypts it before returning the client.
guard(tool, opts?): PolicyDecision
로드된 정책에 대해 도구 호출을 평가합니다.
매개변수:
| 이름 | 유형 | 필수 | 설명 |
|---|---|---|---|
tool | string | 예 | 도구 이름(예: "database"). |
opts | GuardOptions | 아니요 | method, args, raiseOnDeny, context를 가진 옵션 객체. |
GuardOptions:
| 필드 | 유형 | 기본값 | 설명 |
|---|---|---|---|
method | string | "*" | 메서드 이름. 도구와 결합되어 "tool:method" 액션을 이룹니다. |
args | object | {} | DLP 스캔과 조건부 평가에 사용되는 인수. |
raiseOnDeny | boolean | false | true이면 거부 시 PolicyDeniedError를 던집니다. |
context | object | - | 매칭에 사용하는 resource 및 tags를 가진 선택적 컨텍스트. |
반환값: effect, reason, policyId, dlpFindings를 가진 PolicyDecision.
close(): Promise<void>
버퍼에 남은 감사 로그를 비우고 리소스를 해제합니다.
PolicyDecision
guard()가 반환합니다.
| 속성 | 유형 | 설명 |
|---|---|---|
effect | string | "allow" 또는 "deny". |
policyId | string or null | 일치한 정책(있는 경우). |
reason | string | 사람이 읽을 수 있는 설명. |
denied | boolean | 편의 속성: effect가 "deny"이면 true. |
dlpFindings | array | 인수에서 발 견된 DLP 일치 항목 목록. |
PolicyDeniedError
raiseOnDeny가 true이고 정책이 액션을 거부하면 guard()가 던집니다.
class PolicyDeniedError extends Error {
readonly decision: PolicyDecision;
}
MCP와 함께 사용
Control Zero는 Model Context Protocol과 자연스럽게 통합됩니다. 다음은 MCP 도구 호출을 정책 적용으로 감싸는 예시입니다.
import { Client, PolicyDeniedError } from '@controlzero/sdk';
const cz = new Client({ policyFile: './controlzero.yaml' });
function callMCPToolGoverned(server: string, tool: string, args: Record<string, unknown>): unknown {
// Evaluate the policy before calling the tool
cz.guard(server, {
method: tool,
args,
raiseOnDeny: true,
});
// Policy check passed. Call the tool
return mcpClient.callTool(server, tool, args);
}
LLM 제공자 래퍼
Node.js SDK에는 널리 쓰이는 LLM 제공자 클라이언트에 거버넌스를 추가하는 경량 래퍼가 포함되어 있습니다. 각 래퍼는 제공자의 기본 API 표면을 변경하지 않으면서 호출을 가로채고, 정책을 평가하고, 결정을 기록합니다.
Google AI (Gemini)
현재의 @google/genai 패키지를 사용합니다. wrapGoogle은 단일 모델 인스턴스가 아니라
클라이언트의 .models 네임스페이스를 감싸므로, 모든
generateContent / generateContentStream 호출이 통제됩니다.
import { Client } from '@controlzero/sdk';
import { wrapGoogle } from '@controlzero/sdk/integrations';
import { GoogleGenAI } from '@google/genai';
const cz = new Client({ policyFile: './controlzero.yaml' });
const googleClient = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const models = wrapGoogle(googleClient.models, cz, { agentId: 'my-agent' });
const result = await models.generateContent({
model: 'gemini-2.0-flash',
contents: 'Summarize the quarterly report',
});
console.log(result.text);
Anthropic
import { Client } from '@controlzero/sdk';
import Anthropic from '@anthropic-ai/sdk';
const cz = new Client({ policyFile: './controlzero.yaml' });
const anthropic = new Anthropic({ apiKey: 'your-anthropic-key' });
import { wrapAnthropic } from '@controlzero/sdk/integrations';
const wrappedClient = wrapAnthropic(anthropic, cz);
const message = await wrappedClient.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Summarize the quarterly report' }],
});
console.log(message.content[0].text);
사용 가능한 래퍼
| 함수 | 제공자 | 임포트 경로 |
|---|---|---|
wrapGoogle() | Google AI (Gemini) | @controlzero/sdk/integrations |
wrapAnthropic() | Anthropic (Claude) | @controlzero/sdk/integrations |
wrapOpenAI() | OpenAI | @controlzero/sdk/integrations |
모든 래퍼는 사전 점검(모델 차단, 비용 추정, PII 탐지)을 적용하고 요청을 감사 기록에 남깁니다. 정책이 요청을 거부하면 호출이 제공자에 도달하기 전에 PolicyDeniedError가 던져집니다.
오류 처리
import { Client, PolicyDeniedError } from '@controlzero/sdk';
const client = new Client({ policyFile: './controlzero.yaml' });
try {
client.guard('database', {
method: 'query',
args: { sql: 'SELECT 1' },
raiseOnDeny: true,
});
} catch (error) {
if (error instanceof PolicyDeniedError) {
console.log(`Denied: ${error.decision.reason}`);
}
}
await client.close();
CommonJS 지원
SDK는 ESM과 CommonJS를 모두 지원합니다.
// ESM
import { Client } from '@controlzero/sdk';
// CommonJS
const { Client } = require('@controlzero/sdk');
레거시 이름 ControlZeroClient는 Client에 대한 하위 호환 별칭으로 내보내집니다.
TypeScript
SDK는 TypeScript로 작성되었으며 전체 타입 선언을 함께 제공합니다. 모든 타입은 메인 진입점에서 내보내집니다.
import type { PolicyDecision } from '@controlzero/sdk';
import type { ClientOptions, GuardOptions } from '@controlzero/sdk';