Package Exports
- @blacklake-systems/surface-sdk
Readme
@blacklake-systems/sdk
TypeScript SDK for BlackLake Surface — integrate governance directly into custom agent code.
Note: If you are routing tool calls through the MCP proxy, you do not need this SDK. The proxy handles governance automatically. Use the SDK when you want to call the governance API directly from your own code.
Install
npm install @blacklake-systems/sdkQuick Start
Local Surface (self-hosted)
Run npx @blacklake-systems/cli first to start Surface on your machine, then point the SDK at it:
import { BlackLake } from '@blacklake-systems/sdk';
const bl = new BlackLake({
baseUrl: 'http://localhost:3100',
apiKey: process.env.BLACKLAKE_API_KEY!,
});
// Evaluate governance before executing a tool call
const result = await bl.govern({
agent: 'expense-bot',
tool: 'payments.send',
action: { amount: 4200, vendor: 'Acme Corp' },
});
if (result.decision === 'allow') {
// proceed with tool call
}Cloud Surface
Sign up at console.blacklake.systems to get your API key:
import { BlackLake } from '@blacklake-systems/sdk';
const bl = new BlackLake({
apiKey: process.env.BLACKLAKE_API_KEY!,
// baseUrl defaults to https://api.blacklake.systems
});API Reference
new BlackLake(config)
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
— | Your BlackLake API key (required) |
baseUrl |
string |
https://api.blacklake.systems |
API base URL. Override only for local development (e.g. http://localhost:3100). |
bl.govern(request)
Evaluate whether an agent is allowed to invoke a tool.
const result = await bl.govern({
agent: 'expense-bot', // agent name
tool: 'payments.send', // tool name
action: { amount: 4200 }, // optional: tool invocation payload
context: { ip: '10.0.0.1' } // optional: request metadata
});
// result.decision: 'allow' | 'deny' | 'approval_required' | 'default_deny'
// result.evaluation_id: string
// result.policy_id: string | null
// result.reason: string
// result.evaluated_at: string (ISO 8601)
// result.approval_id: string | undefined (set when decision === 'approval_required')bl.agents
await bl.agents.create({ name, environment, risk_classification, description?, approval_mode? });
await bl.agents.list({ environment?, status? });
await bl.agents.get(id);
await bl.agents.update(id, { name?, description?, environment?, risk_classification?, status?, approval_mode? });
await bl.agents.suspend(id);
await bl.agents.activate(id);
await bl.agents.bindTool(agentId, toolId);
await bl.agents.listTools(agentId); // returns ToolBinding[] — each item has { binding_id, binding_created_at, tool: Tool }
await bl.agents.unbindTool(agentId, toolId);bl.tools
await bl.tools.create({ name, risk_classification, description? });
await bl.tools.list();
await bl.tools.get(id);bl.policies
await bl.policies.create({ name, priority, outcome, agent_selector?, tool_selector?, enabled? });
await bl.policies.list();
await bl.policies.get(id);
await bl.policies.update(id, { name?, priority?, outcome?, agent_selector?, tool_selector?, enabled? });
await bl.policies.delete(id);bl.evaluations
await bl.evaluations.list({ agent_id?, tool_id?, outcome?, limit?, offset? });
await bl.evaluations.get(id);bl.organisation
await bl.organisation.get(); // fetch the current organisation (derived from the API key)
await bl.organisation.delete(confirmation); // permanently delete the organisation; pass the organisation's exact name as confirmationbl.apiKeys
await bl.apiKeys.list(); // returns { keys: ApiKey[] } — each item has { id, name, key_suffix, created_at, revoked_at }
await bl.apiKeys.create('prod-key'); // returns { id, name, key, created_at, warning } — the raw key is shown ONCE; store it securely
await bl.apiKeys.revoke(id); // sets revoked_at on the key; the API rejects revoking the key in usebl.approvals
await bl.approvals.list({ status?, agent_id?, tool_id?, limit?, offset? }); // returns PaginatedResponse<Approval>
await bl.approvals.get(id);
await bl.approvals.status(id); // returns ApprovalStatusResponse — lightweight poll target
await bl.approvals.approve(id, { decided_by, reason });
await bl.approvals.reject(id, { decided_by, reason });
await bl.approvals.wait(id, { interval?, timeout? }); // polls status until approved/rejected/expired; throws BlackLakeError on timeoutwait() defaults to polling every 2 000 ms with a 5-minute total timeout. Returns the fully-populated Approval once the status leaves 'pending'.
bl.webhooks
await bl.webhooks.list(); // returns { webhooks: Webhook[] }
await bl.webhooks.create({ url, events, enabled? }); // returns CreatedWebhook — the raw signing secret is shown ONCE; store it securely
await bl.webhooks.get(id);
await bl.webhooks.update(id, { url?, events?, enabled? });
await bl.webhooks.delete(id);
await bl.webhooks.listDeliveries(id, { limit?, offset? }); // returns PaginatedResponse<WebhookDelivery>Webhooks fire on 'approval.created', 'approval.approved', and 'approval.rejected'. Each request is signed with HMAC-SHA256; the signature is sent in the X-BlackLake-Signature header.
Error Handling
import { BlackLake, BlackLakeError } from '@blacklake-systems/sdk';
try {
await bl.govern({ agent: 'unknown', tool: 'unknown' });
} catch (err) {
if (err instanceof BlackLakeError) {
console.error(err.status, err.code, err.message);
}
}Documentation
Full documentation at blacklake.systems/docs.