Skip to content

Runtime Validation Strategy with Zod

Aichaku uses Zod for runtime validation of external data inputs, providing type safety beyond TypeScript’s compile-time checks. This document explains our validation strategy and implementation decisions.

TypeScript only provides compile-time type checking. When Aichaku:

  • Loads configuration files from disk
  • Parses YAML/JSON content
  • Processes CLI arguments
  • Receives data from external sources

…there’s no guarantee the data matches our expected types at runtime. Users might manually edit config files, or files might become corrupted.

After evaluating alternatives (see Deno Validation Libraries Report), we selected Zod because:

  1. Best Developer Experience

    • Exceptional TypeScript inference
    • Single source of truth (schema generates types)
    • Extensive documentation
    • Large, active community
  2. Perfect Fit for CLI Tools

    • Bundle size (60kb) irrelevant for CLI applications
    • Server-side focus matches our use case
    • Mature, stable, production-ready
  3. Ecosystem Compatibility

    • Works seamlessly with Deno via npm: specifier
    • Already using npm packages (MCP SDK)
    • Rich ecosystem of integrations
  4. Superior to Alternatives

    • Typebox: More verbose, less intuitive API
    • Valibot: Younger ecosystem, limited documentation
    • ArkType: Still in beta, too risky
    • Native validation: Would require excessive boilerplate
External World → [Zod Validation] → Your Code → [TypeScript Types] → Internal Logic

We validate data at system boundaries, not everywhere:

  1. Configuration Files (src/utils/config-manager.ts)

    const rawConfig = JSON.parse(content);
    const config = AichakuConfigSchema.parse(rawConfig); // Validated!
  2. YAML Parsing (src/utils/yaml-config-reader.ts)

    const parsed = parse(yamlContent);
    return YamlConfigSchema.parse(parsed); // Safe parsing
  3. CLI Arguments (src/utils/config-schemas.ts)

    export const CLIArgsSchema = z.object({
    help: z.boolean().optional(),
    version: z.boolean().optional(),
    // ... validated argument structure
    });
  • Internal function calls between modules
  • Test code
  • Generated or computed data
  • Type-safe builder patterns

All Zod schemas are centralized in src/utils/config-schemas.ts:

// Example: Project Configuration Schema
export const ProjectConfigSchema = z.object({
version: z.string().regex(/^\d+\.\d+\.\d+$/),
selected: z.object({
methodologies: z.array(z.string()).default([]),
standards: z.array(z.string()).default([]),
principles: z.array(z.string()).default([]),
agents: z.array(z.string()).default([]),
}),
metadata: z.object({
lastUpdated: z.string().datetime().optional(),
projectPath: z.string().optional(),
}).optional(),
});
// Type inference - no duplication!
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
try {
const config = ConfigSchema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("Invalid configuration:", error.format());
}
}
const result = ConfigSchema.safeParse(data);
if (result.success) {
// Use result.data
} else {
// Handle result.error
}
const ConfigSchema = z.object({
theme: z.string().default("light"),
items: z.array(z.string()).default([]),
});
const BaseSchema = z.object({ id: z.string() });
const ExtendedSchema = BaseSchema.extend({
name: z.string(),
description: z.string(),
});
  • Overhead: <5ms for typical config validation
  • Bundle Size: 60kb (acceptable for CLI tools)
  • Runtime Impact: Negligible for non-hot-path validation

When adding validation to existing code:

  1. Identify External Data Entry Points

    • File reads
    • User inputs
    • Network responses
  2. Create Schema

    const DataSchema = z.object({
    // Define expected structure
    });
  3. Replace Type Assertions

    // Before (unsafe)
    const data = JSON.parse(content) as DataType;
    // After (safe)
    const data = DataSchema.parse(JSON.parse(content));
  4. Handle Validation Errors

    try {
    const data = DataSchema.parse(input);
    } catch (error) {
    // Provide user-friendly error message
    }

Zod validation helps prevent:

  • Injection Attacks (OWASP A03): Validates and sanitizes inputs
  • Data Integrity Issues (OWASP A08): Ensures data structure compliance
  • Type Confusion Vulnerabilities: Runtime type enforcement

Potential areas for expanded validation:

  • MCP server responses
  • File system operation results
  • Plugin/extension data
  • Network API responses