Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ VECTOR_DB_ENABLED=false
# VECTOR_DB_TYPE=pinecone|weaviate|chroma
# VECTOR_DB_API_KEY=your-api-key
# VECTOR_DB_ENDPOINT=https://your-instance.vectordb.com

# Sandbox Configuration (Optional)
# Set SANDBOX_ENABLED=true to enable sandbox features for diagnostics and simulations
SANDBOX_ENABLED=false
# SANDBOX_DEFAULT_TIMEOUT=30000 # Timeout in milliseconds (default: 30000)
# SANDBOX_LOG_LEVEL=info # Log level for sandbox execution (debug|info|warn|error)
146 changes: 146 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This project includes comprehensive quality and hardening features:

- **Config Validation**: Type-safe configuration with Zod validation
- **Telemetry**: Optional OpenTelemetry integration (no vendor lock-in)
- **Sandboxes**: Isolated execution environments for diagnostics and simulations
- **Security Scanning**: Trivy vulnerability scanning and SBOM generation
- **Pre-commit Hooks**: Automatic linting and formatting
- **Dependency Management**: Renovate for automated updates
Expand Down Expand Up @@ -59,6 +60,7 @@ The project uses Zod for runtime configuration validation. Configuration is load
- `TELEMETRY_*`: Optional telemetry settings
- `AI_*`: Optional AI/ML settings
- `VECTOR_DB_*`: Optional vector database settings
- `SANDBOX_*`: Optional sandbox settings

**Example:**

Expand Down Expand Up @@ -120,6 +122,142 @@ const span = tracer.startSpan('my-operation');
span.end();
```

## Sandboxes

### Overview

Sandboxes provide isolated execution environments for running diagnostics and simulations. This feature is useful for transparency testing, scenario validation, and safe execution of potentially long-running or resource-intensive operations.

**Enable Sandboxes:**

```bash
# Set in .env file
SANDBOX_ENABLED=true
SANDBOX_DEFAULT_TIMEOUT=30000 # Optional - timeout in milliseconds
SANDBOX_LOG_LEVEL=info # Optional - debug|info|warn|error
```

### Features

- **Isolated Execution**: Run code in isolated sandbox environments
- **Timeout Control**: Automatic timeout handling for long-running operations
- **Error Handling**: Comprehensive error capture and reporting
- **Logging**: Built-in logging with configurable levels
- **Diagnostics**: Run transparency validation tests
- **Simulations**: Execute scenario-based testing

### Basic Usage

```typescript
import { runInSandbox } from './sandbox';

// Simple sandbox execution
const result = await runInSandbox('my-sandbox', async () => {
// Your code here
return { status: 'success' };
});

if (result.success) {
console.log('Result:', result.data);
console.log('Duration:', result.duration, 'ms');
} else {
console.error('Error:', result.error);
}
```

### Diagnostic Tests

Run diagnostic tests for transparency validation:

```typescript
import { runDiagnostics } from './sandbox/diagnostics';

const results = await runDiagnostics([
{
name: 'config-validation',
description: 'Validate configuration',
run: async () => {
// Your validation logic
return true; // pass or false to fail
},
},
{
name: 'connectivity-check',
description: 'Check connectivity',
run: async () => {
// Check external services
return true;
},
},
]);

// Results contain: name, status, message, duration, metadata
results.forEach((r) => {
console.log(`${r.name}: ${r.status} (${r.duration}ms)`);
});
```

### Simulations

Run scenario-based simulations:

```typescript
import { runSimulations } from './sandbox/simulation';

const results = await runSimulations([
{
name: 'high-load-scenario',
description: 'Test under high load',
input: { events: 1000 },
run: async (input) => {
// Process events
return { processed: input.events };
},
validate: (output) => {
// Optional validation
return output.processed === 1000;
},
},
]);

// Results contain: scenarioName, success, output, duration, validated
results.forEach((r) => {
console.log(`${r.scenarioName}: ${r.success ? 'PASS' : 'FAIL'}`);
});
```

### Advanced Usage

For more control, use the Sandbox class directly:

```typescript
import { Sandbox } from './sandbox';

const sandbox = new Sandbox({
name: 'custom-sandbox',
timeout: 60000, // 60 seconds
logLevel: 'debug',
isolated: true,
});

const result = await sandbox.execute(async () => {
// Your code here
return { result: 'data' };
});

// Access sandbox logs
const logs = sandbox.getLogs();
```

### Examples

See `src/sandbox/examples.ts` for complete working examples including:

- Basic sandbox execution
- Transparency diagnostics
- Load testing simulations
- Error handling scenarios

## Development

### Building
Expand Down Expand Up @@ -256,7 +394,15 @@ Check Issues tab for the Renovate Dependency Dashboard
│ │ └── validator.ts # Smoke test script
│ ├── telemetry/ # OpenTelemetry integration
│ │ └── index.ts # Tracer and logger setup
│ ├── sandbox/ # Sandbox execution environments
│ │ ├── index.ts # Core sandbox functionality
│ │ ├── diagnostics.ts # Diagnostic test runner
│ │ ├── simulation.ts # Simulation scenario runner
│ │ └── examples.ts # Usage examples
│ └── index.ts # Main entry point
├── test/ # Test files
│ ├── basic.test.js # Basic tests
│ └── sandbox.test.js # Sandbox tests
├── .github/
│ └── workflows/ # GitHub Actions workflows
├── .husky/ # Git hooks
Expand Down
14 changes: 14 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ export const ConfigSchema = z.object({
endpoint: z.string().url().optional(),
})
.optional(),

// Sandbox settings (optional)
sandbox: z
.object({
enabled: z.coerce.boolean().default(false),
defaultTimeout: z.coerce.number().int().positive().default(30000),
logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
})
.optional(),
});

export type Config = z.infer<typeof ConfigSchema>;
Expand Down Expand Up @@ -70,6 +79,11 @@ export function loadConfig(): Config {
apiKey: process.env.VECTOR_DB_API_KEY,
endpoint: process.env.VECTOR_DB_ENDPOINT,
},
sandbox: {
enabled: process.env.SANDBOX_ENABLED,
defaultTimeout: process.env.SANDBOX_DEFAULT_TIMEOUT,
logLevel: process.env.SANDBOX_LOG_LEVEL,
},
};

// Parse and validate configuration
Expand Down
146 changes: 146 additions & 0 deletions src/sandbox/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* Diagnostic runner for transparency testing
*/

import { Sandbox, SandboxResult } from './index';
import { createLogger } from '../telemetry';

const logger = createLogger('diagnostics');

/**
* Diagnostic test result
*/
export interface DiagnosticResult {
name: string;
status: 'pass' | 'fail' | 'skip';
message: string;
duration: number;
metadata?: Record<string, unknown>;
}

/**
* Diagnostic test definition
*/
export interface DiagnosticTest {
name: string;
description: string;
run: () => Promise<boolean> | boolean;
skip?: boolean;
}

/**
* Diagnostic runner for executing transparency tests
*/
export class DiagnosticRunner {
private tests: DiagnosticTest[] = [];
private sandbox: Sandbox;

constructor(name: string = 'diagnostic-runner') {
this.sandbox = new Sandbox({
name,
timeout: 60000, // 60s for diagnostics
logLevel: 'info',
});
}

/**
* Register a diagnostic test
*/
registerTest(test: DiagnosticTest): void {
this.tests.push(test);
logger.debug(`Registered diagnostic test: ${test.name}`);
}

/**
* Run all registered diagnostic tests
*/
async runAll(): Promise<DiagnosticResult[]> {
logger.info(`Running ${this.tests.length} diagnostic tests`);
const results: DiagnosticResult[] = [];

for (const test of this.tests) {
if (test.skip) {
results.push({
name: test.name,
status: 'skip',
message: 'Test skipped',
duration: 0,
});
continue;
}

const result = await this.runTest(test);
results.push(result);
}

const passed = results.filter((r) => r.status === 'pass').length;
const failed = results.filter((r) => r.status === 'fail').length;
const skipped = results.filter((r) => r.status === 'skip').length;

logger.info('Diagnostic tests completed', {
total: results.length,
passed,
failed,
skipped,
});

return results;
}

/**
* Run a single diagnostic test
*/
private async runTest(test: DiagnosticTest): Promise<DiagnosticResult> {
const startTime = Date.now();

try {
const result: SandboxResult<boolean> = await this.sandbox.execute(async () => {
return await test.run();
});

const duration = Date.now() - startTime;

if (!result.success) {
return {
name: test.name,
status: 'fail',
message: result.error?.message || 'Test execution failed',
duration,
metadata: { error: result.error },
};
}

return {
name: test.name,
status: result.data === true ? 'pass' : 'fail',
message: result.data === true ? 'Test passed' : 'Test failed',
duration,
};
} catch (error) {
const duration = Date.now() - startTime;
return {
name: test.name,
status: 'fail',
message: error instanceof Error ? error.message : 'Unknown error',
duration,
metadata: { error },
};
}
}

/**
* Get number of registered tests
*/
getTestCount(): number {
return this.tests.length;
}
}

/**
* Helper function to create and run diagnostics
*/
export async function runDiagnostics(tests: DiagnosticTest[]): Promise<DiagnosticResult[]> {
const runner = new DiagnosticRunner();
tests.forEach((test) => runner.registerTest(test));
return runner.runAll();
}
Loading