Skip to main content

Airgap Service Architecture

The Airgap Service is a standalone service that combines Mock Engine and Protocol Server functionality into a single binary for air-gapped environments. It uses SQLite for storage and removes all authentication and multi-tenancy requirements.

Overview

The Airgap Service enables SureStage to run in environments without internet connectivity or external dependencies:

  • Single binary: No microservice dependencies
  • SQLite backend: Embedded database, no PostgreSQL required
  • No authentication: All requests use a synthetic admin user
  • No multi-tenancy: Single tenant mode
  • Rate limited: 120 requests/minute per IP
  • Portable: Configurable SQLite database path

Architecture

Key Differences from Cloud Services

FeatureCloud ServicesAirgap Service
DatabasePostgreSQLSQLite (configurable path)
AuthenticationJWT + OAuthNoopAuthGuard (no auth)
Multi-tenancyTenantGuard + tenant_idSingle tenant mode
User ContextReal users from JWTSynthetic admin user
Rate LimitingTier-based (10-1000/min)120 req/min per IP
External ServicesAI, Identity, OrganizationSelf-contained
DeploymentKubernetes podsSingle binary

SQLite Backend

AirgapDatabaseModule

The Airgap Service uses a custom database module that replaces TypeORM's PostgreSQL connection with SQLite:

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: process.env.AIRGAP_DB_PATH || './airgap.db',
entities: [Instance, Route, Response, MockState, ProtocolConfig],
synchronize: true, // Auto-migrate schema
logging: false
})
]
})
export class AirgapDatabaseModule {}

Configuration:

  • AIRGAP_DB_PATH: Path to SQLite database file (default: ./airgap.db)
  • Auto-creates database if it doesn't exist
  • Schema migrations run automatically on startup

Schema Differences

PostgreSQL-specific features are removed or adapted:

FeaturePostgreSQLSQLite
UUIDsuuid_generate_v4()GUID() or application-generated
JSONBNative JSONBText column + JSON parse
ArraysNative arraysJSON-serialized strings
Full-text searchtsvectorLIKE queries
TransactionsFull ACIDLimited (no nested transactions)

Authentication & Authorization

NoopAuthGuard

Replaces JwtAuthGuard and TenantGuard with a no-op implementation:

@Injectable()
export class NoopAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
return true; // Always allow
}
}

Applied globally in main.ts:

app.useGlobalGuards(new NoopAuthGuard());

AirgapUserMiddleware

Injects a synthetic user into every request:

@Injectable()
export class AirgapUserMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
req.user = {
userId: 'airgap_admin',
email: 'admin@airgap.local',
tenantId: 'airgap_tenant',
roles: ['admin']
};
next();
}
}

All services expecting req.user from JWT now receive this synthetic user.

Mock Engine Modules

The Airgap Service imports all Mock Engine modules:

Simulations Module

  • Create, start, stop, delete Sandbox simulations
  • Manage simulation lifecycle
  • No tenant isolation (single tenant)

Routes Module

  • Define routes for HTTP, GraphQL, gRPC, WebSocket
  • Path and method matching
  • Route priority and fallback handling

Responses Module

  • Response templates with dynamic placeholders
  • Status code, headers, body configuration
  • Template rendering (Handlebars)

Mock State Module

  • Stateful mock scenarios
  • Key-value state storage per instance
  • State reset and initialization

Mock Engine Module

  • Core request matching and response resolution
  • Protocol-agnostic request handling
  • State evaluation and transformation

Flows Module

  • Multi-step test flows
  • Request chaining and variable passing
  • Flow execution and result tracking

Protocol Server Modules

The Airgap Service imports all Protocol Server modules:

Protocol Configs Module

  • HTTP, GraphQL, gRPC, WebSocket configurations
  • Protocol-specific settings (CORS, rate limits, timeouts)

Port Allocation Module

  • Dynamic port assignment for simulations
  • Port pool management
  • Conflict detection

Protocol Server Module

  • Protocol adapter instantiation (HTTP, GraphQL, gRPC, WebSocket)
  • Request routing to Mock Engine
  • Protocol-specific request/response transformation

Protocol Health Module

  • Health check endpoints per protocol
  • Simulation readiness checks
  • Liveness probes

AirgapSharedProtocolModule

Replaces PostgreSQL-specific queries in Protocol Server with SQLite-compatible versions:

Example - Port Allocation:

PostgreSQL:

SELECT port FROM port_allocation WHERE instance_id = $1 FOR UPDATE;

SQLite (via AirgapSharedProtocolModule):

SELECT port FROM port_allocation WHERE instance_id = ?;
-- Manual locking via application logic

Changes:

  • Replace FOR UPDATE with application-level locking
  • Replace RETURNING * with separate SELECT after INSERT
  • Replace array queries with JSON-serialized columns

Rate Limiting

Global Rate Limit

120 requests per minute per IP address:

@Injectable()
export class AirgapRateLimitGuard implements CanActivate {
private requests = new Map<string, number[]>();

canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest();
const ip = req.ip;
const now = Date.now();
const windowStart = now - 60000; // 1 minute

// Get requests from this IP in the last minute
const ipRequests = this.requests.get(ip) || [];
const recentRequests = ipRequests.filter(t => t > windowStart);

if (recentRequests.length >= 120) {
throw new HttpException('Rate limit exceeded', 429);
}

recentRequests.push(now);
this.requests.set(ip, recentRequests);
return true;
}
}

Applied globally in main.ts:

app.useGlobalGuards(new AirgapRateLimitGuard());

Configuration

Environment Variables

VariableDescriptionDefault
AIRGAP_DB_PATHPath to SQLite database file./airgap.db
AIRGAP_PORTHTTP port3000
AIRGAP_RATE_LIMITRequests per minute120
AIRGAP_LOG_LEVELLog verbosityinfo

Startup

# Default configuration
./airgap-service

# Custom database path
AIRGAP_DB_PATH=/data/airgap.db ./airgap-service

# Custom port and rate limit
AIRGAP_PORT=8080 AIRGAP_RATE_LIMIT=200 ./airgap-service

Deployment

Binary Distribution

The Airgap Service is distributed as a single binary:

# Linux
./airgap-service-linux-amd64

# macOS
./airgap-service-darwin-arm64

# Windows
airgap-service-windows-amd64.exe

Docker

FROM node:20-alpine
WORKDIR /app
COPY airgap-service /app/
VOLUME ["/data"]
ENV AIRGAP_DB_PATH=/data/airgap.db
EXPOSE 3000
CMD ["./airgap-service"]

Run:

docker run -v ./data:/data -p 3000:3000 surestage/airgap-service

Systemd Service

[Unit]
Description=SureStage Airgap Service
After=network.target

[Service]
Type=simple
User=surestage
WorkingDirectory=/opt/surestage
ExecStart=/opt/surestage/airgap-service
Environment="AIRGAP_DB_PATH=/var/lib/surestage/airgap.db"
Restart=on-failure

[Install]
WantedBy=multi-user.target

Data Persistence

Database Backups

SQLite database can be backed up with a simple file copy:

# Stop service
systemctl stop airgap-service

# Copy database
cp /var/lib/surestage/airgap.db /backup/airgap-$(date +%Y%m%d).db

# Start service
systemctl start airgap-service

Export/Import

Export simulations and routes to JSON:

curl http://localhost:3000/instances/export > instances.json

Import on another Airgap instance:

curl -X POST http://localhost:3000/instances/import \
-H "Content-Type: application/json" \
-d @instances.json

Limitations

Not Supported

The following features are not available in Airgap mode:

  • User authentication: No login, no JWT tokens
  • Multi-tenancy: Single tenant only
  • AI features: No AI Service integration
  • OAuth providers: No external auth
  • SSO: No SAML or OIDC
  • SCIM provisioning: No user sync
  • Audit logs: No audit trail (can enable file logging)
  • Feature flags: No dynamic flag management
  • Analytics: No usage metrics
  • External webhooks: No outbound HTTP to external services

Workarounds

  • Authentication: Use network-level access controls (VPN, firewall)
  • Multi-tenancy: Run multiple Airgap services with different database files
  • AI features: Pre-generate responses in cloud, export to Airgap
  • Audit logs: Enable file-based logging with log rotation

Security Considerations

No Built-in Auth

Since Airgap Service has no authentication, you must secure access via:

  • Firewall rules: Restrict to internal network
  • VPN: Require VPN connection
  • Reverse proxy: Use nginx/Apache with basic auth
  • Network segmentation: Deploy in isolated network

SQLite Permissions

Secure the SQLite database file:

chmod 600 /var/lib/surestage/airgap.db
chown surestage:surestage /var/lib/surestage/airgap.db

Rate Limiting

The 120 req/min limit protects against accidental DoS but is not sufficient for hostile environments. Use additional rate limiting at the network level if exposed to untrusted networks.

Troubleshooting

Database Locked

SQLite locks the entire database file during writes. If you see "database is locked" errors:

  • Reduce concurrency (limit parallel requests)
  • Increase SQLite timeout: PRAGMA busy_timeout = 5000;
  • Use WAL mode: PRAGMA journal_mode = WAL;

Port Conflicts

If Airgap Service fails to start due to port conflicts:

# Check what's using port 3000
lsof -i :3000

# Use different port
AIRGAP_PORT=8080 ./airgap-service

Database Corruption

If SQLite database is corrupted:

# Attempt recovery
sqlite3 airgap.db ".recover" | sqlite3 airgap-recovered.db

# Or restore from backup
cp /backup/airgap-20260320.db /var/lib/surestage/airgap.db