npm package: @runplex/sylo
Description: TypeScript SDK for the Sylo Security Gateway
Latest version: 0.1.1

# @runplex/sylo

TypeScript SDK for the [Sylo](https://sylo.runplex.dev) Security Gateway — secure your AI agents' API access with credential injection, policy enforcement, and audit logging.

## Install

```bash
npm install @runplex/sylo
```

## Quick Start

```typescript
import { SyloClient, credential } from "@runplex/sylo";
import { Sandbox } from "@e2b/code-interpreter";

const sylo = new SyloClient({
  apiUrl: "https://sylo.runplex.dev",
  developerSecret: process.env.SYLO_SECRET!,
});

// 1. Create a sandbox session with credentials + policies
const { token } = await sylo.createSandboxToken({
  tenantId: "acme",
  userId: "user_123",
  expiresIn: "10m",
  credentials: [
    credential("github", process.env.GITHUB_TOKEN!),
    credential("openai", process.env.OPENAI_API_KEY!),
  ],
  // mitm is auto-inferred from credential domains
  policies: {
    default: "deny",  // credentialed domains are implicitly allowed
    rules: [
      { domain: "api.github.com", methods: ["GET"], paths: ["/repos/acme/*"] },
    ],
    rateLimit: { "api.github.com": "50/min" },
  },
});

// 2. Launch E2B sandbox with gateway env vars
const sandbox = await Sandbox.create({
  template: "your-template",
  envs: sylo.sandboxEnv(token),
});

// 3. Query audit log after the agent runs
const { events } = await sylo.queryAudit({ tenantId: "acme" });

// 4. Revoke when done
await sylo.revokeSession(token);
```

## API Reference

### `new SyloClient(options)`

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `apiUrl` | `string` | Yes | Platform API URL (e.g. `https://sylo.runplex.dev`) |
| `developerSecret` | `string` | Yes | Your developer secret (`sylo_sk_...`) |
| `gatewayHost` | `string` | No | Gateway tunnel host. Defaults to `{apiUrl hostname}:8443` |
| `fetch` | `typeof fetch` | No | Custom fetch implementation (for testing) |

### `createSandboxToken(request)`

Create a sandbox session with credentials, policies, and configuration.

```typescript
const { token, expiresAt } = await sylo.createSandboxToken({
  tenantId: "acme",           // required — your customer's identifier
  userId: "user_123",         // optional — end user who triggered the agent
  expiresIn: "5m",            // optional — TTL (default: "5m")
  credentials: [...],         // optional — credentials to inject
  mitm: [...],                // optional — auto-inferred from credentials + policy domains
  passthrough: [...],         // optional — domains to forward without interception
  policies: {...},            // optional — allow/deny rules + rate limits
  pii: {...},                 // optional — PII scanning config
  limits: {...},              // optional — request/duration limits
  policyWebhook: "https://...", // optional — custom policy decision endpoint
});
```

**Returns:** `{ token: string, expiresAt: string }`

### `sandboxEnv(token)`

Get environment variables for the sandbox. Pass these when creating an E2B sandbox.

```typescript
const env = sylo.sandboxEnv(token);
// {
//   SYLO_TOKEN: "sylo_stk_...",
//   SYLO_GATEWAY: "sylo.runplex.dev:8443",
//   SYLO_GATEWAY_API: "https://sylo.runplex.dev"
// }
```

### `revokeSession(token)`

Immediately revoke a session. The sandbox loses access.

```typescript
await sylo.revokeSession("sylo_stk_...");
```

### `listSessions(options?)`

List sessions for your workspace.

```typescript
const { sessions, total } = await sylo.listSessions({
  status: "active",  // optional: "pending" | "active" | "expired"
  limit: 50,         // optional (default: 50)
  offset: 0,         // optional (default: 0)
});
```

### `queryAudit(options?)`

Query the audit log — every proxied request through the gateway.

```typescript
const { events, total } = await sylo.queryAudit({
  tenantId: "acme",           // optional
  destination: "api.github.com", // optional
  policyDecision: "deny",    // optional: "allow" | "deny" | "would-block"
  limit: 100,                // optional (default: 100)
  offset: 0,                 // optional (default: 0)
});
```

### Error Handling

All methods throw `SyloError` on failure:

```typescript
import { SyloError } from "@runplex/sylo";

try {
  await sylo.createSandboxToken({ tenantId: "acme" });
} catch (err) {
  if (err instanceof SyloError) {
    console.error(err.status); // HTTP status (0 for network errors)
    console.error(err.code);   // "unauthorized", "invalid_request", etc.
    console.error(err.message);
  }
}
```

## Security Profiles

Security profiles let you save credential + policy configurations and reuse them across sessions. Create once, reference by name:

```typescript
// Create a profile — MITM auto-inferred, policies saved
const profile = await sylo.createProfile({
  name: "code-review-agent",
  profileData: {
    credentials: [
      credential("anthropic", process.env.ANTHROPIC_API_KEY!),
      credential("github", process.env.GITHUB_TOKEN!),
    ],
    policies: {
      default: "deny",
      rules: [{ domain: "api.github.com", methods: ["GET"] }],
    },
  },
});

// Use it in sessions — no need to repeat credentials + policies
const session = await sylo.createSandboxToken({
  tenantId: "customer-123",
  securityProfile: profile.name,
});
```

### `createProfile(request)`

```typescript
const profile = await sylo.createProfile({
  name: "my-profile",        // unique within workspace
  profileData: {
    credentials: [...],       // credential() helper works here
    policies: {...},          // saved with the profile
    passthrough: [...],       // optional
    pii: {...},               // optional
    limits: {...},            // optional
    policyWebhook: "...",     // optional
    shadowMode: true,         // optional
  },
});
```

Credentials with a `value` are encrypted and saved. Set `value: null` for runtime slots that must be filled per-session:

```typescript
// Profile with one saved credential + one runtime slot
const profile = await sylo.createProfile({
  name: "multi-tenant-agent",
  profileData: {
    credentials: [
      credential("anthropic", process.env.ANTHROPIC_API_KEY!),  // saved
      { name: "tenant-api", domain: "api.example.com", header: "Authorization", value: null },  // runtime slot
    ],
    policies: { default: "deny" },
  },
});

// Each session fills the runtime slot with the tenant's key
const session = await sylo.createSandboxToken({
  tenantId: "customer-123",
  securityProfile: "multi-tenant-agent",
  credentials: [
    { name: "tenant-api", domain: "api.example.com", header: "Authorization", value: tenantApiKey },
  ],
});
```

### `listProfiles()`

```typescript
const { profiles } = await sylo.listProfiles();
```

### `getProfile(id)`

```typescript
const profile = await sylo.getProfile("profile-id");
```

### `updateProfile(id, request)`

```typescript
await sylo.updateProfile(profile.id, {
  name: "renamed",
  profileData: { ... },
});
```

### `deleteProfile(id)`

```typescript
await sylo.deleteProfile(profile.id);
```

## Credential Injection

Credentials are encrypted at rest and injected into requests by the gateway. The sandbox never sees the raw credential values.

### `credential()` helper

The easiest way to add credentials for common APIs. Handles domain, auth header, prefix, and env var automatically:

```typescript
import { credential } from "@runplex/sylo";

credentials: [
  credential("anthropic", process.env.ANTHROPIC_API_KEY!),
  credential("openai", process.env.OPENAI_API_KEY!),
  credential("github", process.env.GITHUB_TOKEN!),
]
```

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

Override name or env var when needed:
```typescript
credential("github", token, { name: "github-read", envVar: "GITHUB_READ" })
```

### Full config

For custom APIs or AWS SigV4, use the full credential object:

```typescript
credentials: [
  { name: "custom", domain: "api.example.com", header: "Authorization", value: "Bearer ..." },
]
```

**Named mode** — multiple credentials per domain:
```typescript
credentials: [
  credential("github", readToken, { name: "github-read", envVar: "GITHUB_READ" }),
  credential("github", writeToken, { name: "github-write", envVar: "GITHUB_WRITE" }),
]
// In the sandbox:
// GITHUB_READ=sylo_cred:github-read
// GITHUB_WRITE=sylo_cred:github-write
```

## Policies

Control which requests the agent can make:

```typescript
policies: {
  default: "deny",  // credentialed domains are implicitly allowed
  rules: [
    // rules restrict — limit methods/paths on allowed domains
    { domain: "api.github.com", methods: ["GET"], paths: ["/repos/acme/*"] },
  ],
  rateLimit: {
    "api.github.com": "50/min",
    "api.openai.com": "20/min",
  },
}
```

With `default: "deny"`, domains that have credentials are automatically allowed without needing explicit rules. Policy rules only add restrictions (limit methods, paths, or rate limits).