@isava/sentinel-sdk
v2.1.0
Published
Official JavaScript/TypeScript SDK for the Sentinel Watch Security Platform
Maintainers
Readme
sentinel-watch-sdk
Official JavaScript/TypeScript SDK for the Sentinel Watch Security Platform.
Built to Stripe SDK standards — fully typed, tree-shakeable, zero runtime dependencies, works in Node 18+, browsers, and edge runtimes.
Install
npm install sentinel-watch-sdk
# or
yarn add sentinel-watch-sdk
# or
pnpm add sentinel-watch-sdkOptional: Install axios if you prefer it over the native Fetch API:
npm install axiosQuick Start
import SentinelClient from 'sentinel-watch-sdk';
const sentinel = new SentinelClient({
baseUrl: 'https://api.your-sentinel.io/api',
timeout: 15_000,
maxRetries: 3,
});
// 1. Login — token stored automatically, no manual header management
const { user } = await sentinel.auth.login('[email protected]', 'p@ssw0rd');
console.log(user.role); // 'security_analyst'
console.log(user.dashboardRoute); // '/analyst'
// 2. Work with typed resources
const { data: alerts, total } = await sentinel.alerts.list({
severity: 'critical',
status: 'open',
limit: 20,
});
// 3. Shorthand actions
await sentinel.alerts.investigate(alerts[0]._id);
await sentinel.alerts.resolve(alerts[0]._id);
// 4. Dashboard + analyst summary in one call
const [stats, summary] = await Promise.all([
sentinel.dashboard.stats(),
sentinel.analyst.summary(),
]);
console.log(stats.alerts.open); // e.g. 14
console.log(summary.vulnerabilities.open); // e.g. 5Configuration
const sentinel = new SentinelClient({
// Required in production
baseUrl: 'https://api.your-sentinel.io/api',
// Pre-set token (useful in server-side apps with stored sessions)
token: process.env.SENTINEL_TOKEN,
// Request timeout in ms (default: 30_000)
timeout: 15_000,
// Max retry attempts for 5xx errors (default: 2, exponential backoff)
maxRetries: 3,
// 'auto' | 'fetch' | 'axios' (default: 'auto')
transport: 'axios',
// Verbose request/response logging — dev only
debug: process.env.NODE_ENV === 'development',
});Error Handling
Every error the SDK throws is an instance of SentinelError or one of its subclasses. This mirrors the Stripe SDK pattern — catch what you need, let everything else bubble.
import {
SentinelClient,
SentinelError,
AuthenticationError,
PermissionError,
NotFoundError,
ValidationError,
RateLimitError,
BillingError,
NetworkError,
TimeoutError,
isSentinelError,
} from 'sentinel-watch-sdk';
try {
const alert = await sentinel.alerts.get('bad-id');
} catch (err) {
if (err instanceof NotFoundError) {
console.error('Alert not found'); // 404
} else if (err instanceof AuthenticationError) {
// Token expired — redirect to login
sentinel.clearToken();
redirectToLogin();
} else if (err instanceof ValidationError) {
// Server-side field validation failed
err.fieldErrors.forEach(e => console.error(e.field, e.message));
} else if (err instanceof RateLimitError) {
// Back off and retry
await sleep(err.retryAfter ? err.retryAfter * 1000 : 2000);
} else if (err instanceof BillingError) {
console.error('Upgrade your plan to access this feature.');
} else if (err instanceof NetworkError) {
console.error('No internet connection.');
} else if (err instanceof TimeoutError) {
console.error('Request timed out — try again.');
} else if (isSentinelError(err)) {
// All other Sentinel errors
console.error(err.code, err.statusCode, err.message);
} else {
throw err; // Unknown — rethrow
}
}Error classes
| Class | HTTP status | Code | When |
|-----------------------|-------------|-------------------------|----------------------------------------|
| AuthenticationError | 401 | authentication_error | Token missing, invalid, or expired |
| PermissionError | 403 | permission_denied | Valid token but wrong role |
| NotFoundError | 404 | resource_not_found | Resource doesn't exist |
| ConflictError | 409 | resource_already_exists| Duplicate email, name, etc. |
| ValidationError | 400 | validation_error | Body failed server validation |
| RateLimitError | 429 | rate_limit_error | Too many requests |
| BillingError | 402 | subscription_inactive | Plan limit, trial expired, etc. |
| ApiError | 5xx | api_error | Server threw an unexpected error |
| NetworkError | — | network_error | Fetch failed before response received |
| TimeoutError | — | timeout_error | Request exceeded timeout ms |
Interceptors
// Add a custom header to every request
sentinel.addRequestInterceptor(req => ({
...req,
headers: { ...req.headers, 'X-Tenant-ID': tenantId },
}));
// Log every response with its request ID
sentinel.addResponseInterceptor(res => {
console.log(`[${res.statusCode}] ${res.requestId}`);
return res;
});
// Auto-logout when token expires
sentinel.addErrorInterceptor(err => {
if (err instanceof AuthenticationError) {
sentinel.clearToken();
window.location.href = '/login';
}
throw err;
});All Resources
Core
// ── Auth ──────────────────────────────────────────────────────────────────
await sentinel.auth.login(email, password)
await sentinel.auth.register(name, email, password, orgName?)
await sentinel.auth.me()
await sentinel.auth.forgotPassword(email)
await sentinel.auth.resetPassword(token, newPassword)
await sentinel.auth.changePassword(current, new)
sentinel.auth.logout() // clears token locally
// ── Alerts ────────────────────────────────────────────────────────────────
await sentinel.alerts.list({ severity, status, page, limit })
await sentinel.alerts.get(id)
await sentinel.alerts.create({ title, severity, source, ... })
await sentinel.alerts.update(id, { status, ... })
await sentinel.alerts.investigate(id) // shorthand
await sentinel.alerts.resolve(id) // shorthand
await sentinel.alerts.stats() // → { total, critical, high, open, ... }
await sentinel.alerts.delete(id)
// ── Incidents ─────────────────────────────────────────────────────────────
await sentinel.incidents.list({ status, severity })
await sentinel.incidents.get(id)
await sentinel.incidents.create({ title, severity })
await sentinel.incidents.update(id, { status })
await sentinel.incidents.contain(id) // shorthand → status: 'contained'
await sentinel.incidents.resolve(id) // shorthand → status: 'resolved'
await sentinel.incidents.close(id) // shorthand → status: 'closed'
await sentinel.incidents.stats()
// ── Organizations ─────────────────────────────────────────────────────────
await sentinel.organizations.list()
await sentinel.organizations.pending() // orgs awaiting approval
await sentinel.organizations.get(id)
await sentinel.organizations.approve(id)
await sentinel.organizations.reject(id, reason?)
await sentinel.organizations.suspend(id)
await sentinel.organizations.members(id)
// ── Users ─────────────────────────────────────────────────────────────────
await sentinel.users.list({ role, organizationId })
await sentinel.users.get(id)
await sentinel.users.create({ name, email, password, role })
await sentinel.users.update(id, { name, isActive })
await sentinel.users.assignSystems(id, [systemId1, systemId2])
await sentinel.users.delete(id)
// ── Systems, Threats, Detection Rules ────────────────────────────────────
await sentinel.systems.list()
await sentinel.threats.list({ severity, status })
await sentinel.detectionRules.list({ type, status })
// ── Playbooks ─────────────────────────────────────────────────────────────
await sentinel.playbooks.list()
await sentinel.playbooks.run(id) // executes + increments executionCount
// ── Dashboard ─────────────────────────────────────────────────────────────
await sentinel.dashboard.stats() // global counts
await sentinel.dashboard.reportSummary() // 30-day trends
// ── Billing ───────────────────────────────────────────────────────────────
await sentinel.billing.plans()
await sentinel.billing.subscription()
await sentinel.billing.createOrder('pro') // → { orderId, approvalUrl }
await sentinel.billing.captureOrder(orderId)
await sentinel.billing.cancel()
await sentinel.billing.reactivate()
await sentinel.billing.invoices()Analyst
// ── Summary ───────────────────────────────────────────────────────────────
await sentinel.analyst.summary() // all secondary stats in one call
// ── Vulnerabilities ───────────────────────────────────────────────────────
await sentinel.vulnerabilities.listWithStats() // data + aggregate stats
await sentinel.vulnerabilities.markPatching(id)
await sentinel.vulnerabilities.markPatched(id)
// ── Phishing ──────────────────────────────────────────────────────────────
await sentinel.phishing.list()
await sentinel.phishing.create({ reporter, subject })
await sentinel.phishing.quarantine(id)
await sentinel.phishing.block(id)
await sentinel.phishing.resolve(id)
// ── Malware ───────────────────────────────────────────────────────────────
await sentinel.malware.list()
await sentinel.malware.submit({ hash, fileName, type })
await sentinel.malware.startAnalysis(id)
await sentinel.malware.markComplete(id)
// ── Certificates ──────────────────────────────────────────────────────────
await sentinel.certificates.list()
await sentinel.certificates.create({ name, expiresAt })
await sentinel.certificates.delete(id)
// ── Firewall Changes ──────────────────────────────────────────────────────
await sentinel.firewallChanges.list({ status: 'pending' })
await sentinel.firewallChanges.create({ requester, rule })
await sentinel.firewallChanges.approve(id)
await sentinel.firewallChanges.reject(id)
// ── Threat Hunts ──────────────────────────────────────────────────────────
await sentinel.threatHunts.list()
await sentinel.threatHunts.create({ name, ttps, query })
await sentinel.threatHunts.start(id)
await sentinel.threatHunts.finish(id, findings)
// ── User Activity (UEBA) ──────────────────────────────────────────────────
await sentinel.userActivity.list({ risk: 'high' })
await sentinel.userActivity.review(id)
// ── Compliance ────────────────────────────────────────────────────────────
await sentinel.compliance.stats() // ISO 27001, NIST, GDPR, SOC2, PCI-DSSToken Management
// Option A: Login (token stored automatically)
await sentinel.auth.login(email, password);
// Option B: Pre-load from storage
const sentinel = new SentinelClient({ token: localStorage.getItem('token') });
// Option C: Set after construction
sentinel.setToken(storedToken);
// Chaining
const sentinel = new SentinelClient({ baseUrl })
.setToken(process.env.SENTINEL_TOKEN!);
// Get current token (e.g. to persist after login)
const { token } = await sentinel.auth.login(email, password);
localStorage.setItem('token', token);
// Clear on logout
sentinel.clearToken();TypeScript
All types are exported from the root package:
import type {
Alert, AlertStatus, AlertStats,
Incident, IncidentStatus,
Organization, User, System,
Vulnerability, ThreatHunt,
DashboardStats, AnalystSummary,
PagedResponse, ApiResponse,
SentinelConfig,
} from 'sentinel-watch-sdk';Publishing
npm run build # compile TypeScript → dist/
npm publish # publish [email protected] to npmjs.com