ari-fastify-gateway
Fastify gateway patterns for ARI's loopback-only security boundary
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Fastify gateway patterns for ARI's loopback-only security boundary
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Discord slash commands, approval routing, channel policy, button interaction patterns for OpenClaw/ARI Discord integration
Obsidian vault integration patterns — vault-analyzer.ts, /ari-vault-* commands, morning briefing snippet, PARA structure, read-only enforcement
OpenClaw plugin development patterns — hooks, manifest structure, plugin SDK, APEX/CODEX enforcement
NOVA's P1 PayThePryce pipeline — market signal ingest, card detection, price monitoring, script generation, thumbnail generation, video assembly, approval gate
CHASE's P2 Pryceless Solutions pipeline — lead discovery, 5-criteria audit, LLM qualification, Prompt Forge 4-pass lock, demo generation, outreach approval gate
NOVA's thumbnail generation pipeline — Ideogram V3 via Fal.ai (primary) + DALL-E 3 fallback, 4-variant strategy, Pokemon TCG copyright rules,
| name | ari-fastify-gateway |
| description | Fastify gateway patterns for ARI's loopback-only security boundary |
| triggers | ["gateway setup","fastify route","api endpoint","loopback server"] |
Manage ARI's Fastify-based gateway with enforced loopback-only binding (ADR-001).
Gateway MUST bind to 127.0.0.1 exclusively. This is HARDCODED and non-configurable.
// src/kernel/gateway.ts
import fastify from 'fastify';
const gateway = fastify({
logger: pinoLogger,
});
// HARDCODED - DO NOT CHANGE
const HOST = '127.0.0.1';
const PORT = config.gateway?.port ?? 3141;
await gateway.listen({ host: HOST, port: PORT });
gateway.post('/message', {
schema: {
body: MessageSchema,
response: { 200: ResponseSchema }
},
preHandler: [sanitizeMiddleware, auditMiddleware],
handler: async (request, reply) => {
const sanitized = sanitizer.sanitize(request.body);
await eventBus.emit('message:accepted', sanitized);
return { status: 'accepted', id: sanitized.id };
}
});
gateway.get('/health', async () => ({
status: 'healthy',
version: config.version,
uptime: process.uptime()
}));
gateway.get('/audit/verify', async () => {
const result = await audit.verifyChain();
return { valid: result.valid, events: result.eventCount };
});
const sanitizeMiddleware = async (request) => {
const risk = sanitizer.assessRisk(request.body);
if (risk >= 0.8) {
throw new SecurityError('Request blocked: high risk score');
}
};
const auditMiddleware = async (request) => {
await eventBus.emit('audit:log', {
action: 'request_received',
method: request.method,
path: request.url,
timestamp: new Date().toISOString()
});
};
gateway.setErrorHandler(async (error, request, reply) => {
await eventBus.emit('audit:log', {
action: 'request_error',
error: error.message,
statusCode: error.statusCode || 500
});
reply.status(error.statusCode || 500).send({
error: 'Internal Server Error',
// Never expose internal error details
});
});
# Start gateway
npm run gateway:start
# Test health
curl http://127.0.0.1:3141/health
# Test message (will be sanitized)
curl -X POST http://127.0.0.1:3141/message \
-H "Content-Type: application/json" \
-d '{"content": "test message"}'