RDCP Implementation Guide - Add Runtime Debug Control in 30 MinutesΒΆ
Based on: Runtime Debug Control Protocol (RDCP) v1.0 Specification
Target: Developers adding RDCP compliance to existing applications
Goal: Mechanical implementation requiring zero design decisions
π Prerequisites ChecklistΒΆ
- [ ] Existing application with debug/logging system
- [ ] Web framework that can create API endpoints
- [ ] Node.js/JavaScript environment (adapt patterns for other languages)
- [ ] 30 minutes of development time
ποΈ Implementation BlueprintΒΆ
Step 1: Create Debug Configuration Object (5 minutes)ΒΆ
Requirement: Mutable runtime configuration for debug categories
Implementation (copy exactly):
// File: src/lib/rdcp-debug.js (or adapt to your structure)
// 1. Define your debug categories (replace with your actual categories)
export const DEBUG_CONFIG = {
DATABASE: false,
API_ROUTES: false,
QUERIES: false,
REPORTS: false,
CACHE: false
}
// 2. Performance metrics (defined first to avoid circular dependency)
const metrics = {
callCount: 0,
startTime: Date.now(),
categoryStats: {}
}
// 3. Performance tracking function
const createTrackedDebugger = (category, logFn) => {
return (message, ...args) => {
if (DEBUG_CONFIG[category]) {
metrics.callCount++
metrics.categoryStats[category] = (metrics.categoryStats[category] || 0) + 1
return logFn(message, ...args)
}
}
}
// 4. Create debug functions with performance tracking
export const debug = {
database: createTrackedDebugger('DATABASE', (message, ...args) =>
console.log(`π [DB] ${message}`, ...args)
),
api: createTrackedDebugger('API_ROUTES', (message, ...args) =>
console.log(`π [API] ${message}`, ...args)
),
query: createTrackedDebugger('QUERIES', (message, ...args) =>
console.log(`π [QUERY] ${message}`, ...args)
),
report: createTrackedDebugger('REPORTS', (message, ...args) =>
console.log(`π [REPORT] ${message}`, ...args)
),
cache: createTrackedDebugger('CACHE', (message, ...args) =>
console.log(`π [CACHE] ${message}`, ...args)
)
}
// 5. Runtime control functions
export const enableDebugCategories = (categories) => {
categories.forEach(category => {
if (category in DEBUG_CONFIG) {
DEBUG_CONFIG[category] = true
}
})
}
export const disableDebugCategories = (categories) => {
categories.forEach(category => {
if (category in DEBUG_CONFIG) {
DEBUG_CONFIG[category] = false
}
})
}
export const getDebugStatus = () => ({ ...DEBUG_CONFIG })
// 6. Performance metrics functions
export const getPerformanceMetrics = () => {
const elapsed = (Date.now() - metrics.startTime) / 1000
const rate = elapsed > 0 ? metrics.callCount / elapsed : 0
return {
callsPerSecond: rate,
totalCalls: metrics.callCount,
uptime: elapsed,
categoryBreakdown: { ...metrics.categoryStats }
}
}
// Reset metrics function
export const resetMetrics = () => {
metrics.callCount = 0
metrics.startTime = Date.now()
metrics.categoryStats = {}
}
// 7. Future-proofing hooks (add these for enterprise readiness)
const DEBUG_BUDGETS = {
maxLogsPerSecond: 100,
maxConcurrentCategories: 3,
budgetResetInterval: 3600000 // 1 hour
}
// OpenTelemetry integration hook
let otelIntegration = null
export function setOTelIntegration(tracer) {
otelIntegration = tracer
}
// AI anomaly detection hook
let aiEventHandler = null
export function setAIEventHandler(handler) {
aiEventHandler = handler
}
// Audit trail hook
let auditLogger = null
export function setAuditLogger(logger) {
auditLogger = logger
}
// Enhanced debug function with future-proofing
const createEnhancedDebugger = (category, logFn) => {
return (message, ...args) => {
if (DEBUG_CONFIG[category]) {
// Performance tracking
metrics.callCount++
metrics.categoryStats[category] = (metrics.categoryStats[category] || 0) + 1
// OpenTelemetry correlation
let traceContext = {}
if (otelIntegration) {
const span = otelIntegration.trace.getActiveSpan()
if (span) {
traceContext = {
traceId: span.spanContext().traceId,
spanId: span.spanContext().spanId
}
}
}
// Budget enforcement
const rate = metrics.callCount / ((Date.now() - metrics.startTime) / 1000)
if (rate > DEBUG_BUDGETS.maxLogsPerSecond) {
console.warn(`β οΈ Debug rate limit exceeded: ${rate.toFixed(1)} logs/sec`)
// Could auto-disable here in production
}
// AI event emission
if (aiEventHandler) {
aiEventHandler('debug-event', {
category,
message,
metadata: args,
timestamp: Date.now(),
traceContext
})
}
// Audit logging for compliance
if (auditLogger && category === 'DATABASE') {
auditLogger.log({
event: 'debug-output',
category,
timestamp: new Date().toISOString(),
complianceLevel: 'low'
})
}
// Standard console output with trace context
return logFn(message, { ...traceContext, ...args })
}
}
}
// Optional: Use enhanced debugger instead of basic
// Uncomment to enable enterprise features:
// const createTrackedDebugger = createEnhancedDebugger
Step 1.5: Create Shared Utilities (Optional - 2 minutes)ΒΆ
Optimization: Extract helper functions to avoid duplication across endpoints.
// File: lib/rdcp-utils.js
export function getCategoryEmoji(category) {
const emojiMap = {
DATABASE: 'π',
API_ROUTES: 'π',
QUERIES: 'π',
REPORTS: 'π',
CACHE: 'π'
}
return emojiMap[category] || 'π'
}
export function getCategoryDescription(category) {
const descMap = {
DATABASE: 'Database connection and operations',
API_ROUTES: 'HTTP request and response handling',
QUERIES: 'SQL query execution and performance',
REPORTS: 'Report generation and processing',
CACHE: 'Cache operations and hits/misses'
}
return descMap[category] || 'Debug logging category'
}
export function getCategoryTags(category) {
const tagMap = {
DATABASE: ['infrastructure', 'performance'],
API_ROUTES: ['api', 'request-response'],
QUERIES: ['database', 'performance'],
REPORTS: ['business-logic'],
CACHE: ['performance', 'optimization']
}
return tagMap[category] || ['general']
}
// Factory function to create category objects
export function createCategoryInfo(debugConfig) {
return Object.keys(debugConfig).map(id => ({
"id": id,
"name": id.replace('_', ' ').toLowerCase(),
"emoji": getCategoryEmoji(id),
"enabled": debugConfig[id],
"priority": "medium",
"description": getCategoryDescription(id),
"tags": getCategoryTags(id),
"callCount": 0,
"estimatedOverhead": "low"
}))
}
Step 2: Create Required API Endpoints (15 minutes)ΒΆ
Requirement: 5 RDCP-compliant endpoints with exact JSON schemas
2.1 Protocol Discovery EndpointΒΆ
// File: routes/well-known-rdcp.js (adapt path to your framework)
export async function GET(request) {
return Response.json({
"protocol": "rdcp/1.0",
"app": {
"name": "your-app-name", // CHANGE THIS
"version": "1.0.0", // CHANGE THIS
"environment": process.env.NODE_ENV || "development"
},
"endpoints": {
"discovery": "/rdcp/v1/discovery",
"control": "/rdcp/v1/control",
"status": "/rdcp/v1/status",
"health": "/rdcp/v1/health"
},
"capabilities": {
"runtimeToggle": true,
"categoryGranularity": true,
"performanceMonitoring": true,
"temporaryControl": false, // Start simple
"bulkOperations": true,
"budgetEnforcement": true,
"kernelLevelInstrumentation": false, // eBPF ready
"systemCallTracing": false,
"multiTenancy": true
},
"compliance": {
"level": "basic",
"auditTrail": false, // Enable in production
"retentionDays": 90,
"standards": ["gdpr-ready", "sox-ready"],
"extensions": []
},
"integrations": {
"opentelemetry": {
"enabled": false, // Enable when configured
"correlationSupport": true,
"semanticConventions": ["rdcp/1.0"]
},
"aiIntegration": {
"anomalyDetectionEnabled": false,
"predictiveAlertsSupported": true,
"contextEnrichment": true
}
}
})
}
2.2 Debug System DiscoveryΒΆ
// File: app/rdcp/v1/discovery/route.js (Next.js App Router)
// Or: routes/rdcp/v1/discovery.js (Express)
import { DEBUG_CONFIG, getPerformanceMetrics } from '../../../lib/rdcp-debug.js'
import { createCategoryInfo } from '../../../lib/rdcp-utils.js'
export async function GET(request) {
const categories = createCategoryInfo(DEBUG_CONFIG)
const metrics = getPerformanceMetrics()
return Response.json({
"protocol": "rdcp/1.0",
"timestamp": new Date().toISOString(),
"debugSystem": {
"type": "centralized-config",
"implementation": "your-app-rdcp-v1", // CHANGE THIS
"version": "1.0.0",
"features": ["zero-overhead", "emoji-categories"]
},
"categories": categories,
"performance": {
"overhead": {
"cpu": {
"value": 0.1,
"unit": "percent",
"measured": false // Placeholder for now
},
"memory": {
"value": 1048576, // 1MB in bytes
"unit": "bytes",
"measured": false
},
"logsPerSecond": {
"value": metrics.callsPerSecond,
"unit": "per_second",
"measured": true
}
}
},
"metadata": {
"lastReset": new Date().toISOString(),
"configurationHash": "initial",
"complianceLevel": "basic"
}
})
}
Note: If you didn't create the shared utilities (Step 1.5), define the helper functions inline here.
2.3 Runtime Control EndpointΒΆ
// File: app/rdcp/v1/control/route.js (Next.js App Router)
import { enableDebugCategories, disableDebugCategories, getDebugStatus, resetMetrics } from '../../../lib/rdcp-debug.js'
import { validateControlRequest } from '../../../lib/rdcp-validation.js'
import { withRDCPAuth } from '../../../lib/rdcp-auth/index.js'
// Standard error response format
function createValidationError(message) {
return Response.json(
{
error: message,
code: 'RDCP_VALIDATION_ERROR',
protocol: 'rdcp/1.0'
},
{ status: 400 }
)
}
export const POST = withRDCPAuth(async function(request) {
const body = await request.json()
// Validate request using schema
const validation = validateControlRequest(body)
if (!validation.valid) {
return createValidationError(validation.error)
}
const timestamp = new Date().toISOString()
const requestId = body.requestId || `req_${Date.now()}`
const changes = []
// Multi-tenancy support (extract from auth context)
const tenantId = request.headers.get('x-tenant-id') || 'default'
// Audit trail for compliance
const auditEntry = {
timestamp,
requestId,
action: body.action,
categories: body.categories,
operator: body.options?.operator || 'system',
justification: body.options?.reason || 'operational',
tenantId,
complianceRisk: 'low'
}
// Handle different actions
switch (body.action) {
case 'enable':
const categoriesToEnable = Array.isArray(body.categories) ? body.categories : [body.categories]
enableDebugCategories(categoriesToEnable)
changes.push(...categoriesToEnable.map(cat => ({
category: cat,
action: 'enabled',
previousState: false, // Could track this
newState: true,
effectiveTime: timestamp
})))
break
case 'disable':
const categoriesToDisable = Array.isArray(body.categories) ? body.categories : [body.categories]
disableDebugCategories(categoriesToDisable)
changes.push(...categoriesToDisable.map(cat => ({
category: cat,
action: 'disabled',
previousState: true,
newState: false,
effectiveTime: timestamp
})))
break
case 'enable-all':
const allCategories = Object.keys(DEBUG_CONFIG)
enableDebugCategories(allCategories)
changes.push(...allCategories.map(cat => ({
category: cat,
action: 'enabled',
previousState: DEBUG_CONFIG[cat],
newState: true,
effectiveTime: timestamp
})))
break
case 'disable-all':
const allCats = Object.keys(DEBUG_CONFIG)
disableDebugCategories(allCats)
changes.push(...allCats.map(cat => ({
category: cat,
action: 'disabled',
previousState: DEBUG_CONFIG[cat],
newState: false,
effectiveTime: timestamp
})))
break
case 'reset':
const allCatsForReset = Object.keys(DEBUG_CONFIG)
disableDebugCategories(allCatsForReset)
resetMetrics() // Reset performance metrics
changes.push({
category: 'ALL',
action: 'reset',
previousState: 'mixed',
newState: 'disabled',
effectiveTime: timestamp
})
break
default:
return createValidationError(`Unknown action: ${body.action}`)
}
const currentState = getDebugStatus()
return Response.json({
"protocol": "rdcp/1.0",
"requestId": requestId,
"success": true,
"timestamp": timestamp,
"changes": changes,
"currentState": Object.keys(currentState).reduce((acc, key) => {
acc[key] = {
enabled: currentState[key],
temporary: false
}
return acc
}, {}),
"performance": {
"newProjectedOverhead": {
"cpu": "0.1%", // Update based on enabled categories
"memory": "1MB",
"logsPerSecond": 1.0
}
}
})
}
2.4 Status Monitoring EndpointΒΆ
// File: app/rdcp/v1/status/route.js (Next.js App Router)
import { getDebugStatus, getPerformanceMetrics } from '../../../lib/rdcp-debug.js'
export async function GET(request) {
const status = getDebugStatus()
const metrics = getPerformanceMetrics()
const categories = {}
Object.keys(status).forEach(category => {
if (status[category]) {
categories[category] = {
enabled: true,
activeCallCount: 0, // Could track this
callsInLastMinute: 0, // Could track this
totalCallsToday: 0, // Could track this
lastActivity: new Date().toISOString(),
averageLatency: "1ms"
}
}
})
return Response.json({
"protocol": "rdcp/1.0",
"timestamp": new Date().toISOString(),
"categories": categories,
"performance": {
"realtime": {
"logsPerSecond": metrics.callsPerSecond,
"memoryUsage": "1MB",
"cpuImpact": "0.1%"
},
"trends": {
"last5Minutes": {
"averageLogsPerSecond": metrics.callsPerSecond,
"peakLogsPerSecond": metrics.callsPerSecond * 2
}
}
}
})
}
2.5 Health Check EndpointΒΆ
// File: app/rdcp/v1/health/route.js (Next.js App Router)
import { DEBUG_CONFIG } from '../../../lib/rdcp-debug.js'
export async function GET(request) {
return Response.json({
"protocol": "rdcp/1.0",
"status": "healthy",
"timestamp": new Date().toISOString(),
"debugSystemOperational": true,
"details": {
"categoriesResponsive": Object.keys(DEBUG_CONFIG).length,
"categoriesError": 0,
"controlEndpointLatency": "5ms",
"lastSuccessfulReset": new Date().toISOString()
},
"version": {
"protocol": "rdcp/1.0",
"implementation": "your-app-rdcp-v1.0.0" // CHANGE THIS
}
})
}
Step 3: Add Route Registration (5 minutes)ΒΆ
Framework-specific step - register the routes according to your framework:
Next.js App RouterΒΆ
// File structure auto-registers routes:
// .well-known/rdcp/route.js
// rdcp/v1/discovery/route.js
// rdcp/v1/control/route.js
// rdcp/v1/status/route.js
// rdcp/v1/health/route.js
Express.jsΒΆ
// File: server.js
import express from 'express'
import {
DEBUG_CONFIG,
getPerformanceMetrics,
enableDebugCategories,
disableDebugCategories,
getDebugStatus,
resetMetrics
} from './lib/rdcp-debug.js'
import { validateRDCPAuth } from './lib/rdcp-auth/index.js'
import { validateControlRequest } from './lib/rdcp-validation.js'
import { createCategoryInfo } from './lib/rdcp-utils.js'
const app = express()
app.use(express.json())
// Express-specific endpoint handlers
const rdcpDiscovery = (req, res) => {
res.json({
"protocol": "rdcp/1.0",
"app": {
"name": "your-express-app", // CHANGE THIS
"version": "1.0.0", // CHANGE THIS
"environment": process.env.NODE_ENV || "development"
},
"endpoints": {
"discovery": "/rdcp/v1/discovery",
"control": "/rdcp/v1/control",
"status": "/rdcp/v1/status",
"health": "/rdcp/v1/health"
},
"capabilities": {
"runtimeToggle": true,
"categoryGranularity": true,
"performanceMonitoring": true,
"temporaryControl": false,
"bulkOperations": true
},
"compliance": {
"level": "basic",
"extensions": []
}
})
}
const debugDiscovery = (req, res) => {
const categories = createCategoryInfo(DEBUG_CONFIG)
const metrics = getPerformanceMetrics()
res.json({
"protocol": "rdcp/1.0",
"timestamp": new Date().toISOString(),
"debugSystem": {
"type": "centralized-config",
"implementation": "your-app-rdcp-v1", // CHANGE THIS
"version": "1.0.0",
"features": ["zero-overhead", "emoji-categories"]
},
"categories": categories,
"performance": {
"currentOverhead": {
"cpu": "0.1%",
"memory": "1MB",
"logsPerSecond": metrics.callsPerSecond
}
},
"metadata": {
"lastReset": new Date().toISOString(),
"configurationHash": "initial",
"complianceLevel": "basic"
}
})
}
const debugControl = (req, res) => {
// Authentication check
const auth = validateRDCPAuth(req)
if (!auth.valid) {
return res.status(401).json({
error: {
code: 'RDCP_AUTH_FAILED',
message: 'Authentication failed',
protocol: 'rdcp/1.0'
}
})
}
const body = req.body
const validation = validateControlRequest(body)
if (!validation.valid) {
return res.status(400).json({
error: validation.error,
code: 'RDCP_VALIDATION_ERROR',
protocol: 'rdcp/1.0'
})
}
// Handle actions (same logic as Next.js, adapted for Express)
const timestamp = new Date().toISOString()
const requestId = body.requestId || `req_${Date.now()}`
const changes = []
switch (body.action) {
case 'enable':
const toEnable = Array.isArray(body.categories) ? body.categories : [body.categories]
enableDebugCategories(toEnable)
// ... add to changes array
break
// ... other cases
}
res.json({
"protocol": "rdcp/1.0",
"requestId": requestId,
"success": true,
"timestamp": timestamp,
"changes": changes,
"currentState": getDebugStatus()
// ... rest of response
})
}
const debugStatus = (req, res) => {
const status = getDebugStatus()
const metrics = getPerformanceMetrics()
res.json({
"protocol": "rdcp/1.0",
"timestamp": new Date().toISOString(),
"categories": status,
"performance": {
"overhead": {
"logsPerSecond": {
"value": metrics.callsPerSecond,
"unit": "per_second",
"measured": true
}
}
}
})
}
const debugHealth = (req, res) => {
res.json({
"protocol": "rdcp/1.0",
"status": "healthy",
"timestamp": new Date().toISOString(),
"debugSystemOperational": true,
"details": {
"categoriesResponsive": Object.keys(DEBUG_CONFIG).length,
"categoriesError": 0,
"controlEndpointLatency": "5ms",
"lastSuccessfulReset": new Date().toISOString()
},
"version": {
"protocol": "rdcp/1.0",
"implementation": "your-app-rdcp-v1.0.0" // CHANGE THIS
}
})
}
// Register RDCP endpoints
// Note: Helper functions moved to lib/rdcp-utils.js to avoid duplication
app.get('/.well-known/rdcp', rdcpDiscovery)
app.get('/rdcp/v1/discovery', debugDiscovery)
app.post('/rdcp/v1/control', debugControl)
app.get('/rdcp/v1/status', debugStatus)
app.get('/rdcp/v1/health', debugHealth)
FastifyΒΆ
// File: server.js
import Fastify from 'fastify'
import { rdcpDiscovery, debugDiscovery, debugControl, debugStatus, debugHealth } from './rdcp-endpoints.js'
const fastify = Fastify({ logger: true })
// RDCP endpoints
fastify.get('/.well-known/rdcp', rdcpDiscovery)
fastify.get('/rdcp/v1/discovery', debugDiscovery)
fastify.post('/rdcp/v1/control', debugControl)
fastify.get('/rdcp/v1/status', debugStatus)
fastify.get('/rdcp/v1/health', debugHealth)
Koa.jsΒΆ
// File: server.js
import Koa from 'koa'
import Router from 'koa-router'
import { rdcpDiscovery, debugDiscovery, debugControl, debugStatus, debugHealth } from './rdcp-endpoints.js'
const app = new Koa()
const router = new Router()
// RDCP endpoints
router.get('/.well-known/rdcp', rdcpDiscovery)
router.get('/rdcp/v1/discovery', debugDiscovery)
router.post('/rdcp/v1/control', debugControl)
router.get('/rdcp/v1/status', debugStatus)
router.get('/rdcp/v1/health', debugHealth)
app.use(router.routes())
Step 4: Add Authentication Based on Security Level (5-10 minutes)ΒΆ
Option A: Basic Level (API Key) - Development/InternalΒΆ
// File: lib/rdcp-auth.js
import crypto from 'crypto'
const RDCP_API_KEY = process.env.RDCP_API_KEY || 'dev-key-change-in-production-min-32-chars'
function extractApiKey(request) {
// Framework detection - Next.js has headers.get(), Express has headers[]
if (typeof request.headers.get === 'function') {
// Next.js Request object
const authHeader = request.headers.get('authorization')
const apiKeyHeader = request.headers.get('x-api-key')
return authHeader?.replace('Bearer ', '') || apiKeyHeader
} else {
// Express/Node.js request object
const authHeader = request.headers['authorization']
const apiKeyHeader = request.headers['x-api-key']
return authHeader?.replace('Bearer ', '') || apiKeyHeader
}
}
export function validateRDCPAuth(request) {
const providedKey = extractApiKey(request)
// Basic security checks
if (!providedKey || providedKey.length < 32) {
return false
}
if (!RDCP_API_KEY || RDCP_API_KEY.length < 32) {
console.error('RDCP_API_KEY must be at least 32 characters for security')
return false
}
try {
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(RDCP_API_KEY),
Buffer.from(providedKey)
)
} catch (error) {
// Keys are different lengths - return false without revealing why
return false
}
}
// Standard error response for authentication failure
function createAuthError() {
return Response.json(
{
error: 'Authentication required',
code: 'RDCP_AUTH_REQUIRED',
protocol: 'rdcp/1.0'
},
{ status: 401 }
)
}
// Middleware for protected endpoints
export function withRDCPAuth(handler) {
return async function(request) {
const ok = validateRDCPAuth(request)
if (!ok) {
return createAuthError()
}
return handler(request)
}
}
Option B: Standard Level (JWT Bearer Token) - Production SaaSΒΆ
// File: lib/rdcp-auth-jwt.js
import jwt from 'jsonwebtoken'
const JWT_SECRET = process.env.JWT_SECRET || 'change-in-production'
export function validateRDCPAuth(request) {
const authHeader = request.headers.get('authorization')
if (!authHeader?.startsWith('Bearer ')) {
return {
valid: false,
error: 'Missing Bearer token'
}
}
const token = authHeader.substring(7)
try {
const decoded = jwt.verify(token, JWT_SECRET)
// Return standard auth context
return {
valid: true,
method: 'bearer',
userId: decoded.sub || decoded.email,
tenantId: decoded.org_id || decoded.tenant,
scopes: decoded.scopes || ['discovery', 'status'],
sessionId: decoded.session_id,
expiresAt: new Date(decoded.exp * 1000).toISOString()
}
} catch (error) {
return {
valid: false,
error: error.message
}
}
}
// Standard error response
export function createAuthError() {
return Response.json(
{
error: {
code: 'RDCP_AUTH_FAILED',
message: 'Authentication failed',
method: 'bearer',
protocol: 'rdcp/1.0'
}
},
{ status: 401 }
)
}
Option C: Enterprise Level (mTLS) - Regulated IndustriesΒΆ
// File: lib/rdcp-auth-mtls.js
// File: lib/rdcp-auth-mtls.js
import { X509Certificate } from 'crypto'
export function validateRDCPAuth(request) {
// Extract client certificate from request
const certHeader = request.headers.get('x-client-cert')
const certSubject = request.headers.get('x-rdcp-cert-subject')
if (!certHeader) {
return {
valid: false,
error: 'Client certificate required'
}
}
try {
// Validate certificate
const cert = new X509Certificate(Buffer.from(certHeader, 'base64'))
// Check certificate validity
const now = new Date()
if (now < cert.validFrom || now > cert.validTo) {
return {
valid: false,
error: 'Certificate expired or not yet valid'
}
}
// Extract identity from certificate
const subject = cert.subject
const cn = subject.match(/CN=([^,]+)/)?.[1]
// Also check for JWT token for additional context
const authHeader = request.headers.get('authorization')
let tokenContext = {}
if (authHeader?.startsWith('Bearer ')) {
// Hybrid mode: mTLS + JWT
const token = authHeader.substring(7)
const decoded = jwt.verify(token, JWT_SECRET)
tokenContext = {
userId: decoded.sub,
scopes: decoded.scopes
}
}
return {
valid: true,
method: 'mtls',
userId: tokenContext.userId || cn,
tenantId: extractTenantFromCN(cn),
scopes: tokenContext.scopes || ['admin'],
sessionId: cert.fingerprint,
metadata: {
certSubject: cert.subject,
certIssuer: cert.issuer,
certFingerprint: cert.fingerprint
}
}
} catch (error) {
return {
valid: false,
error: error.message
}
}
}
function extractTenantFromCN(cn) {
// Example: CN=client.tenant123.example.com
const match = cn?.match(/\.([^.]+)\.example\.com$/)
return match?.[1] || 'default'
}
Unified Auth Adapter (Recommended)ΒΆ
// File: lib/rdcp-auth/index.js
// Selects the appropriate auth validator based on RDCP_AUTH_LEVEL
import { validateRDCPAuth as validateApiKey } from '../rdcp-auth.js'
import { validateRDCPAuth as validateJwt } from '../rdcp-auth-jwt.js'
import { validateRDCPAuth as validateMtls } from '../rdcp-auth-mtls.js'
const LEVEL = (process.env.RDCP_AUTH_LEVEL || 'basic').toLowerCase()
function normalize(result, method) {
if (result && typeof result === 'object' && 'valid' in result) return result
return {
valid: !!result,
method,
scopes: ['discovery', 'status', 'control', 'health']
}
}
export function validateRDCPAuth(request) {
switch (LEVEL) {
case 'enterprise':
return normalize(validateMtls(request), 'mtls')
case 'standard':
case 'bearer':
return normalize(validateJwt(request), 'bearer')
case 'basic':
default:
return normalize(validateApiKey(request), 'api-key')
}
}
export function withRDCPAuth(handler) {
return async function(request) {
const auth = validateRDCPAuth(request)
if (!auth.valid) {
return Response.json(
{ error: { code: 'RDCP_AUTH_FAILED', message: 'Authentication failed', protocol: 'rdcp/1.0' } },
{ status: 401 }
)
}
// Attach auth context for downstream usage
request.rdcpAuth = auth
return handler(request)
}
}
β Verification StepsΒΆ
Test Your ImplementationΒΆ
Basic Functionality TestsΒΆ
# 1. Discovery endpoint (should return RDCP info)
curl -s http://localhost:3000/.well-known/rdcp | jq
# Expected: JSON with protocol, app, endpoints, capabilities
# 2. Debug system info
curl -s http://localhost:3000/rdcp/v1/discovery | jq
# Expected: JSON with categories, performance, metadata
# 3. Enable debug category
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "X-API-Key: dev-key-change-in-production-min-32-chars" \
-H "Content-Type: application/json" \
-d '{"action":"enable","categories":["DATABASE"]}' | jq
# Expected: Success response with changes array
# 4. Check status (should show DATABASE enabled)
curl -s http://localhost:3000/rdcp/v1/status | jq
# Expected: categories object with DATABASE entry
# 5. Health check
curl -s http://localhost:3000/rdcp/v1/health | jq
# Expected: status: "healthy", debugSystemOperational: true
Error Handling TestsΒΆ
# Test authentication failure
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "Content-Type: application/json" \
-d '{"action":"enable","categories":["DATABASE"]}' | jq
# Expected: 401 Unauthorized with RDCP_AUTH_REQUIRED code
# Test invalid API key
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "X-API-Key: short-key" \
-H "Content-Type: application/json" \
-d '{"action":"enable","categories":["DATABASE"]}' | jq
# Expected: 401 Unauthorized
# Test invalid action
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "X-API-Key: dev-key-change-in-production-min-32-chars" \
-H "Content-Type: application/json" \
-d '{"action":"invalid","categories":["DATABASE"]}' | jq
# Expected: 400 Bad Request with error message
# Test missing required fields
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "X-API-Key: dev-key-change-in-production-min-32-chars" \
-H "Content-Type: application/json" \
-d '{"action":"enable"}' | jq
# Expected: 400 Bad Request with missing fields error
# Test invalid category
curl -X POST http://localhost:3000/rdcp/v1/control \
-H "X-API-Key: dev-key-change-in-production-min-32-chars" \
-H "Content-Type: application/json" \
-d '{"action":"enable","categories":["INVALID_CATEGORY"]}' | jq
# Expected: Success but no changes (invalid categories ignored)
Verify Debug OutputΒΆ
// In your application code
import { debug } from './lib/rdcp-debug.js'
// This should now be controlled by RDCP
debug.database('Connection pool initialized')
debug.api('Processing request', { method: 'GET', path: '/users' })
π Success CriteriaΒΆ
- [ ] All 5 endpoints return valid JSON responses
- [ ] Debug categories can be enabled/disabled via API
- [ ] Debug output appears/disappears based on runtime control
- [ ] No application restart required for debug changes
- [ ] Performance metrics are tracked (even if basic)
- [ ] API key authentication works
π Enhancement PathΒΆ
After basic implementation works:
- Enhanced Performance Tracking: Add memory usage, CPU impact measurement
- Temporary Controls: Add time-based auto-disable functionality
- Advanced Authentication: JWT tokens, role-based access
- Integration: OpenTelemetry, Prometheus metrics export
- UI: Build admin interface consuming these APIs
The key: Start with this mechanical implementation, then enhance based on real usage.
π Future-Proofing & Industry TrendsΒΆ
The basic implementation includes hooks for emerging industry trends. These are disabled by default but prevent architectural lock-in:
OpenTelemetry IntegrationΒΆ
Industry Trend: OpenTelemetry is becoming the standard for telemetry data.
Built-in Hook: The enhanced debugger includes trace correlation:
// Enable OpenTelemetry correlation
import { trace } from '@opentelemetry/api'
import { setOTelIntegration } from './lib/rdcp-debug.js'
setOTelIntegration({ trace })
// Now all debug logs include traceId and spanId automatically
Cost & Performance BudgetsΒΆ
Industry Trend: Organizations need cost control for observability data.
Built-in Hook: Budget enforcement prevents runaway logging:
// Already enforced in createEnhancedDebugger
DEBUG_BUDGETS.maxLogsPerSecond = 50 // Adjust for your needs
// Auto-warns when exceeded, can auto-disable in production
AI-Driven Anomaly DetectionΒΆ
Industry Trend: AI is becoming central to observability.
Built-in Hook: Event emission for AI systems:
// Connect your AI system
import { setAIEventHandler } from './lib/rdcp-debug.js'
setAIEventHandler((event, data) => {
aiSystem.analyze(event, data)
// AI can detect patterns and suggest debug enables
})
Compliance & Audit TrailΒΆ
Industry Trend: GDPR, SOX compliance becoming mandatory.
Built-in Hook: Audit logging for all debug changes:
// Enable audit trail
import { setAuditLogger } from './lib/rdcp-debug.js'
setAuditLogger({
log: (entry) => {
// Store in compliance-approved system
complianceDB.insert(entry)
}
})
Multi-Tenancy Support (RDCP Standard)ΒΆ
Industry Trend: Cloud-native applications need tenant isolation.
RDCP Protocol Requirements:
Standard Tenant Context HeadersΒΆ
RDCP defines standard headers for tenant context, agnostic of auth system:
X-RDCP-Tenant-ID: org_2a1b3c4d
X-RDCP-Isolation-Level: organization | namespace | process | global
X-RDCP-Tenant-Name: Acme Corp (optional)
Protocol-Level Tenant HandlingΒΆ
// RDCP Standard: Extract tenant from standard headers
function extractTenantContext(request) {
return {
tenantId: request.headers.get('x-rdcp-tenant-id') || 'default',
isolationLevel: request.headers.get('x-rdcp-isolation-level') || 'global',
tenantName: request.headers.get('x-rdcp-tenant-name')
}
}
// RDCP Standard: Tenant-scoped configuration
const TENANT_DEBUG_CONFIGS = new Map()
function getTenantDebugConfig(tenantId) {
if (!TENANT_DEBUG_CONFIGS.has(tenantId)) {
TENANT_DEBUG_CONFIGS.set(tenantId, {
DATABASE: false,
API_ROUTES: false,
QUERIES: false,
REPORTS: false,
CACHE: false
})
}
return TENANT_DEBUG_CONFIGS.get(tenantId)
}
RDCP Standard Endpoint ResponseΒΆ
All RDCP endpoints MUST include tenant context:
// Standard RDCP response with tenant context
{
"protocol": "rdcp/1.0",
"tenant": {
"id": "extracted-tenant-id",
"isolationLevel": "organization",
"scope": "tenant-isolated" | "global"
},
// ... rest of response
}
Auth System Integration PatternsΒΆ
Pattern 1: JWT-Based Systems:
// Generic JWT integration
function setTenantHeaders(request, jwtPayload) {
request.headers.set('x-rdcp-tenant-id', jwtPayload.org_id || jwtPayload.tenant_id)
request.headers.set('x-rdcp-isolation-level', 'organization')
}
Pattern 2: Session-Based Systems:
// Generic session integration
function setTenantHeaders(request, session) {
request.headers.set('x-rdcp-tenant-id', session.organizationId)
request.headers.set('x-rdcp-isolation-level', 'organization')
}
Pattern 3: API Key Systems:
// Generic API key integration
function setTenantHeaders(request, apiKeyMetadata) {
request.headers.set('x-rdcp-tenant-id', apiKeyMetadata.tenantId)
request.headers.set('x-rdcp-isolation-level', apiKeyMetadata.isolationLevel)
}
SecFlo-Specific Multi-Tenancy ImplementationΒΆ
Note: This section shows how SecFlo implements the RDCP standard using Clerk:
// File: src/lib/secflo-rdcp-adapter.js
// SecFlo's adapter to convert Clerk auth to RDCP standard headers
import { auth } from '@clerk/nextjs'
// SecFlo: Convert Clerk context to RDCP standard headers
export function injectRDCPHeaders(request) {
const { orgId, organization } = auth()
// Set RDCP standard headers from Clerk data
request.headers.set('x-rdcp-tenant-id', orgId || 'default')
request.headers.set('x-rdcp-isolation-level', 'organization')
request.headers.set('x-rdcp-tenant-name', organization?.name || '')
return request
}
// File: src/app/rdcp/v1/discovery/route.js
// SecFlo implementation using RDCP standard (Next.js App Router)
import { extractTenantContext, getTenantDebugConfig } from '@/lib/rdcp-debug'
import { injectRDCPHeaders } from '@/lib/secflo-rdcp-adapter'
import { createCategoryInfo } from '@/lib/rdcp-utils'
export async function GET(request) {
// SecFlo: Add Clerk org to standard RDCP headers
request = injectRDCPHeaders(request)
// RDCP Standard: Extract tenant from headers
const tenantContext = extractTenantContext(request)
const tenantConfig = getTenantDebugConfig(tenantContext.tenantId)
return Response.json({
"protocol": "rdcp/1.0",
"tenant": tenantContext, // Standard RDCP tenant object
"categories": createCategoryInfo(tenantConfig),
// ... rest of response
})
}
Tenant-Scoped Control:
// File: api/debug/control/route.js
export const POST = withRDCPAuth(async function(request) {
const { orgId } = auth()
const tenantConfig = getTenantDebugConfig(orgId)
const body = await request.json()
// Apply changes to tenant-specific config
switch (body.action) {
case 'enable':
body.categories.forEach(cat => {
if (cat in tenantConfig) {
tenantConfig[cat] = true
}
})
break
// ... other actions
}
return Response.json({
"protocol": "rdcp/1.0",
"tenant": {
"id": orgId,
"isolation": "organization"
},
"changes": changes.map(change => ({
...change,
"tenantScope": orgId,
"isolationLevel": "organization"
})),
// ... rest of response
})
})
Admin Tool Multi-Tenant Discovery:
// Admin tool can discover and manage multiple tenants
GET /api/admin/tenants/discovery
{
"tenants": [
{
"id": "org_2a1b3c4d",
"name": "Acme Corp",
"rdcpEndpoint": "https://secflo.app/api/debug/",
"debugStatus": {
"DATABASE": true,
"API_ROUTES": false
},
"metrics": {
"logsPerSecond": 2.3,
"activeSince": "2025-09-17T10:30:00Z"
}
},
{
"id": "org_5e6f7g8h",
"name": "TechCorp",
"rdcpEndpoint": "https://secflo.app/api/debug/",
"debugStatus": {
"QUERIES": true
}
}
]
}
Benefits for SecFlo: - Each organization's debug settings are completely isolated - No cross-tenant debug data leakage - Integrates seamlessly with existing Clerk auth - Admin tool can manage debug across all customer organizations - Debug output automatically includes tenant context for filtering
RDCP Tenant Isolation Levels (Protocol Standard)ΒΆ
The RDCP protocol defines four standard isolation levels:
Level | Description | Use Case |
---|---|---|
global |
No tenant isolation, shared configuration | Single-tenant applications |
process |
Process-level isolation | Container-based multi-tenancy |
namespace |
Namespace isolation (k8s) | Cloud-native applications |
organization |
Full organizational isolation | SaaS multi-tenant applications |
Audit Trail Requirements:
When isolationLevel
is not global
, all control operations MUST include:
{
"audit": {
"tenantId": "org_123",
"operator": "user@example.com",
"timestamp": "2025-09-17T10:30:00Z",
"action": "enable",
"justification": "Investigating issue #1234",
"isolationLevel": "organization"
}
}
This separation ensures: - RDCP protocol remains auth-agnostic - Clear abstraction between protocol and implementation - Standard headers enable tool interoperability - SecFlo can use Clerk while others use different auth systems
Server capabilities: rate limiting and auditΒΆ
The server supports optional rate limiting and persistent audit with configurable behavior.
- Rate limiting
- Configuration supports defaultRule, perEndpoint, perTenant
- Standard headers (draft-7) are emitted when enabled; Retry-After is added on limited responses
- Audit
- Sink options: console | file | none (file supports rotation/retention)
- sampleRate: number (0.0β1.0)
- redact: (record) => record (optional redaction)
- failureMode: 'ignore' | 'warn' | 'fail'
- ignore (default): do nothing on write failure
- warn: adapters add Warning: 199 rdcp "audit-write-failed"
- fail: returns RDCP_AUDIT_WRITE_FAILED (500) with details
Example (Express adapter):
adapters.express.createRDCPMiddleware({
authenticator: auth.validateRDCPAuth,
capabilities: {
rateLimit: {
enabled: true,
headers: true,
headersMode: 'draft-7',
defaultRule: { windowMs: 60000, maxRequests: 120 },
perEndpoint: { control: { windowMs: 10000, maxRequests: 10 } },
},
audit: {
enabled: true,
sink: 'file',
sampleRate: 0.25,
failureMode: 'warn',
file: { path: 'rdcp-audit.log', maxBytes: 5*1024*1024, maxFiles: 5 }
}
}
})
eBPF ReadinessΒΆ
Emerging Trend: Kernel-level observability becoming crucial.
Built-in Hook: Capability flags for future eBPF integration:
"capabilities": {
"kernelLevelInstrumentation": false, // Ready when eBPF available
"systemCallTracing": false
}
Why These Matter Now: Adding these hooks later would require protocol version changes and breaking compatibility. Including them dormant in v1.0 ensures smooth evolution as these technologies mature.
π Known Limitations & Future EnhancementsΒΆ
Performance MetricsΒΆ
The current implementation uses placeholder values for CPU and memory impact ("0.1%", "1MB"). These provide a working baseline but should be enhanced with actual monitoring in production environments.
Enhancement path: Integrate with Node.js perf_hooks
API or system monitoring tools like process.cpuUsage()
and process.memoryUsage()
to provide real metrics:
// Future enhancement example
import { performance } from 'perf_hooks'
function getRealMetrics() {
const cpuUsage = process.cpuUsage()
const memUsage = process.memoryUsage()
return {
cpu: `${(cpuUsage.user / 1000000).toFixed(2)}%`,
memory: `${(memUsage.heapUsed / 1024 / 1024).toFixed(1)}MB`,
actualMeasurement: true
}
}
Category Call TrackingΒΆ
Debug call counts per category are tracked in metrics.categoryStats
but not yet fully exposed through the status endpoint. The infrastructure exists but requires integration with the performance monitoring system.
Enhancement path: Connect category statistics to the status endpoint for detailed usage analysis:
// Future enhancement for status endpoint
Object.keys(status).forEach(category => {
if (status[category]) {
categories[category] = {
enabled: true,
activeCallCount: metrics.categoryStats[category] || 0,
callsInLastMinute: calculateRecentCalls(category),
totalCallsToday: metrics.categoryStats[category] || 0,
lastActivity: getLastActivityTime(category),
averageLatency: calculateAverageLatency(category)
}
}
})
Temporary ControlsΒΆ
The protocol supports temporary debug enables with automatic timeouts, but this implementation uses permanent toggles only. Temporary controls would add production safety by automatically disabling debug output after a specified duration.
Enhancement path: Add timeout functionality with automatic category disable:
// Future enhancement for temporary controls
const temporaryEnables = new Map()
export function enableTemporary(category, duration) {
DEBUG_CONFIG[category] = true
// Clear any existing timeout
if (temporaryEnables.has(category)) {
clearTimeout(temporaryEnables.get(category))
}
// Set new timeout
const timeoutId = setTimeout(() => {
DEBUG_CONFIG[category] = false
temporaryEnables.delete(category)
console.log(`β° Auto-disabled debug category: ${category}`)
}, duration * 1000)
temporaryEnables.set(category, timeoutId)
}
Rate LimitingΒΆ
The current implementation doesn't include rate limiting for debug output, which could overwhelm logs in high-traffic scenarios.
Enhancement path: Implement rate limiting per category to prevent log flooding while maintaining visibility.
PersistenceΒΆ
Debug configuration resets on application restart. Production environments might benefit from optional persistence.
Enhancement path: Add optional configuration persistence using environment variables or a configuration file.
Multi-Instance CoordinationΒΆ
In clustered deployments, each instance maintains its own debug configuration. There's no coordination between instances.
Enhancement path: Use Redis or similar for shared configuration state across instances.
Note: These limitations don't prevent successful RDCP implementation. They represent areas where production deployments might enhance the basic implementation based on actual requirements. The mechanical implementation provides a solid foundation that can be extended as needed.
π§ Troubleshooting Common IssuesΒΆ
Port ConflictsΒΆ
CORS Issues (for browser-based admin tools)ΒΆ
// Express.js CORS middleware for development
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key')
if (req.method === 'OPTIONS') {
res.sendStatus(200)
} else {
next()
}
})
Environment Variable IssuesΒΆ
# Create .env file with required variables
echo "RDCP_API_KEY=your-32-character-or-longer-key-here" > .env
echo "NODE_ENV=development" >> .env
Docker DeploymentΒΆ
# Dockerfile example
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# Environment variables for RDCP
# RDCP_API_KEY must be set at runtime via -e flag or .env file
# Must be 32+ characters for security
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["npm", "start"]
# Run container with API key
docker run -e RDCP_API_KEY="your-32-character-or-longer-secure-key-here" -p 3000:3000 your-app
Debug Output Not AppearingΒΆ
// Verify debug configuration is mutable
console.log('DEBUG_CONFIG before:', DEBUG_CONFIG)
DEBUG_CONFIG.DATABASE = true
console.log('DEBUG_CONFIG after:', DEBUG_CONFIG)
debug.database('Test message') // Should appear
π Request Validation SchemasΒΆ
// File: lib/rdcp-validation.js
import { DEBUG_CONFIG } from './rdcp-debug.js'
const controlRequestSchema = {
type: 'object',
required: ['action', 'categories'],
properties: {
action: {
enum: ['enable', 'disable', 'toggle', 'enable-all', 'disable-all', 'reset']
},
categories: {
oneOf: [
{ type: 'array', items: { type: 'string' } },
{ type: 'string' }
]
},
requestId: { type: 'string' },
options: {
type: 'object',
properties: {
temporary: { type: 'boolean' },
duration: { type: 'number' },
reason: { type: 'string' }
}
}
},
additionalProperties: false
}
export function validateControlRequest(data) {
// Basic validation implementation
if (!data.action || !data.categories) {
return { valid: false, error: 'Missing required fields: action, categories' }
}
const validActions = ['enable', 'disable', 'toggle', 'enable-all', 'disable-all', 'reset']
if (!validActions.includes(data.action)) {
return { valid: false, error: `Invalid action. Must be one of: ${validActions.join(', ')}` }
}
// Check for non-existent categories (if not bulk action)
if (!['enable-all', 'disable-all', 'reset'].includes(data.action)) {
const categories = Array.isArray(data.categories) ? data.categories : [data.categories]
const validCategories = Object.keys(DEBUG_CONFIG)
const invalidCategories = categories.filter(cat => !validCategories.includes(cat))
if (invalidCategories.length > 0) {
return {
valid: false,
error: `Invalid categories: ${invalidCategories.join(', ')}. Valid categories: ${validCategories.join(', ')}`
}
}
}
return { valid: true }
}
π Integration with Existing Logging SystemsΒΆ
Winston Logger IntegrationΒΆ
// File: lib/rdcp-winston-bridge.js
import winston from 'winston'
const logger = winston.createLogger({
level: 'info',
transports: [new winston.transports.Console()]
})
// Bridge RDCP debug to Winston
export const debug = {
database: createTrackedDebugger('DATABASE', (message, ...args) =>
logger.debug(`π [DB] ${message}`, ...args)
),
api: createTrackedDebugger('API_ROUTES', (message, ...args) =>
logger.debug(`π [API] ${message}`, ...args)
)
// ... other categories
}
Pino Logger IntegrationΒΆ
// File: lib/rdcp-pino-bridge.js
import pino from 'pino'
const logger = pino()
export const debug = {
database: createTrackedDebugger('DATABASE', (message, ...args) =>
logger.debug({ category: 'DATABASE', emoji: 'π' }, message, ...args)
)
// ... other categories
}
β οΈ Common PitfallsΒΆ
- Don't overthink categories - Start with your existing debug areas
- Don't optimize performance tracking initially - Get basic metrics first
- Don't build complex authentication - API key is sufficient to start
- Don't create elaborate schemas - Follow the examples exactly
- Don't add features not in spec - Stick to the standard
- Remember API key length - Must be 32+ characters for security
- Test error cases - Verify authentication and validation work
- Check CORS - Add headers if building browser-based admin tools
Implementation Time: 30 minutes for basic compliance
Enhancement Time: Infinite, based on real needs
Goal: RDCP-compliant application that can be controlled at runtime
This guide should require zero design decisions - just mechanical implementation of the RDCP standard.