Skip to content

Protocols

The protocol layer defines TypeScript interfaces that framework adapters implement. The core engine operates exclusively through these interfaces — it never imports Express, Fastify, NestJS, or Hono.

Framework-agnostic request representation. Adapters wrap their native request objects to implement this interface.

interface GuardRequest {
readonly urlPath: string;
readonly urlScheme: string;
readonly urlFull: string;
urlReplaceScheme(scheme: string): string;
readonly method: string;
readonly clientHost: string | null;
readonly headers: Readonly<Record<string, string>>;
readonly queryParams: Readonly<Record<string, string>>;
body(): Promise<Uint8Array>;
readonly state: GuardRequestState;
readonly scope: Readonly<Record<string, unknown>>;
}
PropertyDescription
urlPathURL path without query string (e.g., /api/users)
urlSchemeProtocol scheme (http or https)
urlFullFull URL including scheme, host, path, and query
urlReplaceScheme(scheme)Returns the full URL with a replaced scheme
methodHTTP method (GET, POST, etc.)
clientHostClient IP address from the socket (before proxy resolution)
headersRequest headers as a readonly string map
queryParamsQuery parameters as a readonly string map
body()Returns the request body as Uint8Array (edge-safe, no Buffer)
stateMutable state bag for passing data between pipeline stages
scopeFramework-specific metadata (read-only)

GuardRequestState holds decorator route IDs and endpoint IDs:

interface GuardRequestState {
guardRouteId?: string;
guardEndpointId?: string;
guardDecorator?: unknown;
[key: string]: unknown;
}

Framework-agnostic response representation.

interface GuardResponse {
readonly statusCode: number;
readonly headers: Record<string, string>;
setHeader(name: string, value: string): void;
readonly body: Uint8Array | null;
readonly bodyText: string | null;
}
PropertyDescription
statusCodeHTTP status code
headersResponse headers
setHeader(name, value)Add or override a header
bodyResponse body as Uint8Array (edge-safe)
bodyTextResponse body as UTF-8 string (convenience accessor)

Creates GuardResponse objects. Each adapter provides its own implementation.

interface GuardResponseFactory {
createResponse(content: string, statusCode: number): GuardResponse;
createRedirectResponse(url: string, statusCode: number): GuardResponse;
}

The contract that security checks use to access middleware state and handlers.

interface GuardMiddlewareProtocol {
readonly config: ResolvedSecurityConfig;
readonly logger: Logger;
lastCloudIpRefresh: number;
suspiciousRequestCounts: Map<string, number>;
readonly eventBus: unknown;
readonly routeResolver: unknown;
readonly responseFactory: unknown;
readonly rateLimitHandler: unknown;
readonly agentHandler: AgentHandlerProtocol | null;
readonly geoIpHandler: GeoIPHandler | null;
readonly guardResponseFactory: GuardResponseFactory;
createErrorResponse(statusCode: number, defaultMessage: string): Promise<GuardResponse>;
refreshCloudIpRanges(): Promise<void>;
}

Geographic IP lookup. The default implementation (IPInfoManager) uses the maxmind package.

interface GeoIPHandler {
readonly isInitialized: boolean;
initialize(): Promise<void>;
initializeRedis(redisHandler: RedisHandlerProtocol): Promise<void>;
initializeAgent(agentHandler: AgentHandlerProtocol): Promise<void>;
getCountry(ip: string): string | null;
}

You can provide a custom implementation or use the simpler geoResolver config option:

const config: SecurityConfig = {
geoResolver: (ip: string) => {
return myGeoDatabase.lookup(ip)?.country ?? null;
},
};

Telemetry and event dispatch interface.

interface AgentHandlerProtocol {
initializeRedis(redisHandler: RedisHandlerProtocol): Promise<void>;
sendEvent(event: unknown): Promise<void>;
sendMetric(metric: unknown): Promise<void>;
start(): Promise<void>;
stop(): Promise<void>;
flushBuffer(): Promise<void>;
getDynamicRules(): Promise<unknown | null>;
healthCheck(): Promise<boolean>;
}

Agent failures are never fatal — all calls are wrapped in try/catch throughout the codebase.

Distributed state operations.

interface RedisHandlerProtocol {
getKey(namespace: string, key: string): Promise<unknown>;
setKey(namespace: string, key: string, value: unknown, ttl?: number | null): Promise<boolean | null>;
delete(namespace: string, key: string): Promise<number | null>;
keys(pattern: string): Promise<string[] | null>;
initialize(): Promise<void>;
getConnection(): AsyncDisposable;
}

The RedisManager class implements this protocol using ioredis. All methods return null when Redis is unavailable, allowing callers to fall back to in-memory state.