Skip to content

TypeScript SDK

Terminal window
npm install @runplex/sylo
import { SyloClient, credential } from "@runplex/sylo";
const sylo = new SyloClient({
apiUrl: "https://sylo.runplex.dev",
developerSecret: process.env.SYLO_SECRET,
gatewayHost: "sylo.runplex.dev:8443", // optional, derived from apiUrl
});
OptionTypeRequiredDescription
apiUrlstringYesPlatform API URL
developerSecretstringYesDeveloper secret (sylo_sk_...)
gatewayHoststringNoGateway tunnel host (default: {apiUrl hostname}:8443)
fetchtypeof fetchNoCustom fetch implementation

Create a session with credentials, policies, and configuration.

const session = await sylo.createSandboxToken({
tenantId: "acme",
userId: "user_123",
expiresIn: "10m",
credentials: [
credential("openai", process.env.OPENAI_API_KEY!),
credential("github", process.env.GITHUB_TOKEN!),
],
// mitm is auto-inferred from credential domains
passthrough: ["your-api.com"],
policies: {
default: "deny", // credentialed domains are implicitly allowed
rateLimit: { "api.openai.com": "20/min" },
},
pii: { action: "redact", patterns: ["ssn", "credit_card"] },
limits: { maxRequests: 1000, maxDuration: "30m" },
});
// Returns: { token: "sylo_stk_...", expiresAt: "2026-...", credentials: [...] }

One-liner for built-in services — handles domain, header, prefix, and env var:

import { credential } from "@runplex/sylo";
credential("openai", process.env.OPENAI_API_KEY!)
// → { name: "openai", domain: "api.openai.com", header: "Authorization", value: "Bearer sk-...", envVar: "OPENAI_API_KEY" }

Built-in services (12): anthropic, openai, github, stripe, linear, slack, huggingface, cohere, replicate, groq, mistral, fireworks.

Override name or env var:

credential("github", token, { name: "github-read", envVar: "GITHUB_READ" })

For custom APIs, use the full config:

{ name: "custom", domain: "api.example.com", header: "Authorization", value: "Bearer ..." }

How credentials and policies work together

Section titled “How credentials and policies work together”
  • Credentials implicitly allow their domain — with default: "deny", you don’t need explicit allow rules for credentialed domains
  • MITM is auto-inferred — domains from credentials and policy rules are automatically intercepted
  • Policy rules restrict — limit methods, paths, or add rate limits on allowed domains
  • Default policy — only applies to domains with no credential

Get environment variables for tunnel mode:

const env = sylo.sandboxEnv(session);
// {
// SYLO_TOKEN: "sylo_stk_...",
// SYLO_GATEWAY: "sylo.runplex.dev:8443",
// SYLO_GATEWAY_API: "https://sylo.runplex.dev",
// NODE_EXTRA_CA_CERTS: "/etc/sylo/ca.crt",
// SSL_CERT_FILE: "/etc/sylo/ca.crt",
// REQUESTS_CA_BUNDLE: "/etc/sylo/ca.crt",
// OPENAI_API_KEY: "sylo_cred:openai", // from credentials[].envVar
// }

Get environment variables for proxy mode:

const env = sylo.proxyEnv(session);
// {
// HTTPS_PROXY: "https://sylo_stk_...:x@sylo.runplex.dev:8443",
// HTTP_PROXY: "https://sylo_stk_...:x@sylo.runplex.dev:8443",
// ...same as sandboxEnv
// }

Save credential + policy configurations and reuse across sessions.

const profile = await sylo.createProfile({
name: "code-review-agent",
profileData: {
credentials: [
credential("anthropic", process.env.ANTHROPIC_API_KEY!), // saved credential
credential("github", process.env.GITHUB_TOKEN!), // saved credential
],
policies: { default: "deny" },
},
});

MITM domains are auto-inferred from credentials and policy rules.

Credentials have two modes:

  • Saved (value: "sk-...") — encrypted in the profile, reused every session
  • Runtime slot (value: null) — must be filled per-session, for tenant-specific keys
// Profile with a runtime slot
const profile = await sylo.createProfile({
name: "multi-tenant",
profileData: {
credentials: [
credential("anthropic", process.env.ANTHROPIC_API_KEY!), // saved
{ name: "tenant-api", envVar: "TENANT_API_KEY",
domain: "api.example.com", header: "Authorization", value: null }, // runtime
],
policies: { default: "deny" },
},
});
// Fill runtime slots when creating sessions
const session = await sylo.createSandboxToken({
tenantId: "customer-123",
securityProfile: "multi-tenant",
credentials: [
{ name: "tenant-api", domain: "api.example.com",
header: "Authorization", value: `Bearer ${tenantKey}` },
],
});
const { profiles } = await sylo.listProfiles();
const profile = await sylo.getProfile("profile-id");
await sylo.updateProfile(profile.id, {
name: "renamed",
profileData: { ... },
});
await sylo.deleteProfile(profile.id);

Immediately revoke a session:

await sylo.revokeSession("sylo_stk_...");
const { sessions, total } = await sylo.listSessions({
status: "active",
limit: 50,
offset: 0,
});
const { events, total } = await sylo.queryAudit({
tenantId: "acme",
destination: "api.openai.com",
policyDecision: "allow",
limit: 100,
offset: 0,
});
import { SyloError } from "@runplex/sylo";
try {
await sylo.createSandboxToken({ tenantId: "acme" });
} catch (err) {
if (err instanceof SyloError) {
console.error(err.status); // HTTP status
console.error(err.code); // "unauthorized", "invalid_request", etc.
console.error(err.message);
}
}