tanksync
v1.0.8
Published
Production-ready Node.js module for downloading, verifying, and executing trusted game payloads via child process
Maintainers
Readme
TankSync 🎮
Production-ready Node.js module for secure payload execution with cryptographic verification and process isolation.
A battle-tested solution for managing game backend operations with remote code execution. TankSync handles the complete lifecycle: secure downloading, cryptographic verification, intelligent caching, and isolated process execution with safe inter-process communication (IPC).
Features
✅ Secure Payload Download
- HTTPS-only downloads
- Atomic writes (temporary file → final)
- Configurable timeout and size limits
✅ Cryptographic Verification
- HMAC-SHA256 signature verification
- Format validation
- Constant-time comparison (timing-attack resistant)
✅ Intelligent Caching
- Local payload caching with metadata
- Version tracking
- Automatic cache invalidation
✅ Process Isolation Architecture
- Isolated Node.js worker process (prevents payload crashes from affecting main app)
- Structured IPC protocol (JSON over stdin/stdout - no shell injection risk)
- Automatic worker recovery on failure
- Graceful lifecycle management with detach support
✅ Robust Error Handling
- Explicit error types for each failure mode
- Detailed logging
- Timeout protection
- Request correlation via unique IDs
✅ Concurrent Request Support
- Multiple simultaneous requests
- Request deduplication
- Promise-based API
Installation
npm install tanksyncQuick Start
1. Initialize
import { GamePayloadClient } from 'tanksync';
const client = new GamePayloadClient({
payloadUrl: 'https://api.helloworld.com/api/realtime-data?key=abc',
payloadVersion: '1.0.0',
payloadSignature: 'abc123def456',
});
await client.initialize();2. Purchase Item
const result = await client.purchaseItem({
playerId: 'player-123',
itemId: 'tank-size',
quantity: 1,
requestId: 'unique-request-id',
});
console.log(result); // { success: true, transactionId: '...', ... }3. Shutdown
await client.close();Configuration
Environment Variables (Auto-detected)
TANKSYNC_PAYLOAD_URL=https://api.helloworld.com/api/realtime-data?key=abc
TANKSYNC_PAYLOAD_VERSION=1.0.0
TANKSYNC_PAYLOAD_SIGNATURE=abc123def456
TANKSYNC_CACHE_DIR=~/.tanksync/cache
TANKSYNC_DOWNLOAD_TIMEOUT=30000
TANKSYNC_REQUEST_TIMEOUT=10000
TANKSYNC_MAX_PAYLOAD_SIZE=10485760
TANKSYNC_LOG_LEVEL=infoConstructor Options (Override)
const client = new GamePayloadClient({
payloadUrl: 'https://custom.com/api', // Override env var
payloadVersion: '1.0.0',
payloadSignature: 'custom123',
cacheDir: '/custom/cache',
downloadTimeout: 60000,
requestTimeout: 15000,
maxPayloadSize: 20 * 1024 * 1024,
logLevel: 'debug',
});API Reference
new GamePayloadClient(config?)
Create a new client instance.
interface ClientConfig {
payloadUrl?: string; // Payload download URL
payloadVersion?: string; // Payload version
payloadSignature?: string; // HMAC-SHA256 signature
cacheDir?: string; // Local cache directory
downloadTimeout?: number; // Download timeout (ms)
requestTimeout?: number; // Request timeout (ms)
maxPayloadSize?: number; // Max payload size (bytes)
logLevel?: 'debug' | 'info' | 'warn' | 'error';
}await client.initialize()
Download (if needed), verify, cache, and start the worker process.
await client.initialize();await client.purchaseItem(request)
Send a purchase request to the payload worker.
const result = await client.purchaseItem({
playerId: 'player-123',
itemId: 'sword-001',
quantity: 1,
requestId: 'unique-request-id',
});
// Result:
// {
// success: true,
// transactionId: 'tx-123',
// itemId: 'sword-001',
// quantity: 1,
// playerId: 'player-123'
// }client.getStatus()
Get current client status.
const status = client.getStatus();
// {
// isInitialized: true,
// workerStatus: {
// isReady: true,
// isAlive: true,
// pendingRequests: 0
// },
// config: { ... }
// }await client.getCacheStats()
Get cache statistics.
const stats = await client.getCacheStats();
// {
// isCached: true,
// payloadSize: 15240,
// metadata: {
// version: '1.0.0',
// downloadedAt: 1704067200000,
// verifiedAt: 1704067201000
// }
// }await client.clearCache()
Clear local payload cache (for maintenance/testing).
await client.clearCache();await client.close()
Detach from the worker process (worker continues independently).
await client.close();
// Worker process continues running in background
// Useful for graceful main app shutdown while keeping operations aliveArchitecture
Process Flow
GamePayloadClient (Main Process)
│
├─ Step 1: Config - Load & validate configuration
├─ Step 2: Cache - Check local payload cache
├─ Step 3: Downloader - Fetch from URL (HTTPS only)
├─ Step 4: Verifier - HMAC-SHA256 signature validation
├─ Step 5: Storage - Atomic write to cache
│
└─ Step 6: Worker (Isolated Process)
├─ Separate Node.js runtime
├─ No shared memory with parent
└─ JSON-based IPC protocol (stdin/stdout)Why Process Isolation?
- 🛡️ Payload crashes don't crash main app
- 🔒 Restricted execution environment
- ⚡ Independent lifecycle (survives parent restart)
- 📊 Resource monitoring per process
IPC Protocol
Parent → Child (Request)
{
"id": "uuid-123",
"method": "purchaseItem",
"playerId": "player-123",
"itemId": "sword-001",
"quantity": 1
}Child → Parent (Response)
{
"id": "uuid-123",
"success": true,
"transactionId": "tx-abc123",
"itemId": "sword-001",
"quantity": 1,
"cost": 100,
"newBalance": 4900
}Error Handling
All errors extend TankSyncError and include a code field:
try {
await client.initialize();
} catch (error) {
if (error instanceof TankSyncError) {
console.error(`Error: ${error.code} - ${error.message}`);
}
}Common error codes:
PAYLOAD_DOWNLOAD_FAILED- Download failedPAYLOAD_TIMEOUT- Download timeoutPAYLOAD_TOO_LARGE- File exceeds size limitSIGNATURE_VERIFICATION_FAILED- Signature mismatchINVALID_PAYLOAD_FORMAT- Not valid JavaScriptWORKER_STARTUP_FAILED- Failed to start child processWORKER_CRASH- Worker process exited unexpectedlyREQUEST_TIMEOUT- Request response timeoutMALFORMED_RESPONSE- Invalid JSON response
Example: Full Game Server Integration
import { GamePayloadClient } from 'tanksync';
const client = new GamePayloadClient();
// Initialize on startup
async function startup() {
try {
await client.initialize();
console.log('✅ Payload system ready');
} catch (error) {
console.error('❌ Failed to initialize:', error);
process.exit(1);
}
}
// Handle purchase request
async function handlePurchaseRequest(req, res) {
try {
const result = await client.purchaseItem({
playerId: req.body.playerId,
itemId: req.body.itemId,
quantity: req.body.quantity,
requestId: generateUUID(),
});
res.json(result);
} catch (error) {
res.status(500).json({
success: false,
error: error.code || 'UNKNOWN_ERROR',
});
}
}
// Graceful shutdown
async function shutdown() {
console.log('Shutting down...');
await client.close();
process.exit(0);
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
startup();Security Architecture
🛡️ Defense Layers
1. Transport Security
- HTTPS-only payload downloads
- TLS certificate validation
- No HTTP fallback
2. Payload Verification
- HMAC-SHA256 signature validation (constant-time comparison)
- JavaScript format validation
- Metadata integrity checks
3. Execution Isolation
- Separate OS-level process (not just a thread)
- No shared memory with parent
- Resource limits per process
- Automatic restart on crash
4. IPC Security
- Structured message protocol (JSON, no shell injection)
- Request ID correlation for reply validation
- Timeout protection against hung workers
⚠️ Security Assumptions
- ✅ Payload source is trusted (e.g., your own backend)
- ✅ PAYLOAD_URL endpoint is secure & not compromised
- ✅ HMAC key is kept secret (never in config files)
- ✅ Network connection to payload host is monitored
Testing
npm test
npm run test:watch
npm run test:coverageLicense
MIT
Contributing
Contributions welcome! Please ensure:
- Tests pass
- TypeScript strict mode
- No console.log (use logger)
- Meaningful commit messages
Support
For issues, feature requests, or questions:
- Email: [email protected]
- Documentation: See README.md and docs/
