[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Config Management Service

Build a Config Management Service

Build a centralized configuration management service with environment-specific overrides, secret encryption, hot-reload, validation, audit logging, and rollback.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 96/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Priya leads platform at a 20-person company running 15 microservices. Configuration is scattered: some in .env files, some in Kubernetes ConfigMaps, some hardcoded. Changing a feature flag requires a redeploy. Secrets are in plaintext in environment variables. Nobody knows which service uses which config keys. When someone changed the wrong Redis URL, three services went down simultaneously. They need centralized config: one source of truth, environment-specific overrides, encrypted secrets, hot-reload without redeploy, and audit trail.

Step 1: Build the Config Service

typescript
// src/config/service.ts — Centralized config management with encryption and hot-reload
import { pool } from "../db";
import { Redis } from "ioredis";
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";

const redis = new Redis(process.env.REDIS_URL!);

interface ConfigEntry {
  key: string;
  value: any;
  type: "string" | "number" | "boolean" | "json" | "secret";
  environment: string;       // "production", "staging", "development", "*" (all)
  service: string;           // "*" for global, or specific service name
  description: string;
  validationRule?: string;   // JSON schema or regex
  updatedBy: string;
  updatedAt: string;
  version: number;
}

const ENCRYPTION_KEY = scryptSync(process.env.CONFIG_MASTER_KEY || "change-me", "salt", 32);

// Get config for a service/environment
export async function getConfig(
  service: string,
  environment: string
): Promise<Record<string, any>> {
  const cacheKey = `config:${service}:${environment}`;
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  // Load configs: global → environment → service → service+environment
  const { rows } = await pool.query(
    `SELECT * FROM config_entries
     WHERE (service = '*' OR service = $1)
       AND (environment = '*' OR environment = $2)
     ORDER BY
       CASE WHEN service = '*' AND environment = '*' THEN 1
            WHEN service = '*' THEN 2
            WHEN environment = '*' THEN 3
            ELSE 4 END`,
    [service, environment]
  );

  const config: Record<string, any> = {};
  for (const row of rows) {
    let value = row.value;
    if (row.type === "secret") value = decrypt(value);
    else if (row.type === "number") value = Number(value);
    else if (row.type === "boolean") value = value === "true";
    else if (row.type === "json") value = JSON.parse(value);
    config[row.key] = value;
  }

  await redis.setex(cacheKey, 30, JSON.stringify(config));  // short TTL for hot-reload
  return config;
}

// Set config value
export async function setConfig(params: {
  key: string;
  value: any;
  type: ConfigEntry["type"];
  environment: string;
  service: string;
  description?: string;
  validationRule?: string;
  updatedBy: string;
}): Promise<void> {
  // Validate value
  if (params.validationRule) {
    const valid = validateValue(params.value, params.validationRule);
    if (!valid) throw new Error(`Validation failed for key ${params.key}`);
  }

  // Encrypt secrets
  let storedValue = String(params.value);
  if (params.type === "secret") storedValue = encrypt(String(params.value));

  // Get current version for audit
  const { rows: [current] } = await pool.query(
    "SELECT version, value FROM config_entries WHERE key = $1 AND environment = $2 AND service = $3",
    [params.key, params.environment, params.service]
  );
  const newVersion = current ? current.version + 1 : 1;

  // Save change history
  if (current) {
    await pool.query(
      `INSERT INTO config_history (key, environment, service, old_value, new_value, version, changed_by, changed_at)
       VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`,
      [params.key, params.environment, params.service, current.value, storedValue, newVersion, params.updatedBy]
    );
  }

  // Upsert config
  await pool.query(
    `INSERT INTO config_entries (key, value, type, environment, service, description, validation_rule, updated_by, version, updated_at)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW())
     ON CONFLICT (key, environment, service) DO UPDATE SET
       value = $2, type = $3, description = COALESCE($6, config_entries.description),
       updated_by = $8, version = $9, updated_at = NOW()`,
    [params.key, storedValue, params.type, params.environment, params.service,
     params.description, params.validationRule, params.updatedBy, newVersion]
  );

  // Invalidate cache and notify services
  await redis.del(`config:${params.service}:${params.environment}`);
  await redis.del(`config:*:${params.environment}`);
  await redis.publish("config:changed", JSON.stringify({
    key: params.key, environment: params.environment, service: params.service, version: newVersion,
  }));
}

// Rollback config to previous version
export async function rollbackConfig(
  key: string, environment: string, service: string, targetVersion: number, userId: string
): Promise<void> {
  const { rows: [history] } = await pool.query(
    "SELECT old_value FROM config_history WHERE key = $1 AND environment = $2 AND service = $3 AND version = $4",
    [key, environment, service, targetVersion]
  );
  if (!history) throw new Error("Version not found");

  const { rows: [current] } = await pool.query(
    "SELECT type FROM config_entries WHERE key = $1 AND environment = $2 AND service = $3",
    [key, environment, service]
  );

  await setConfig({
    key, value: history.old_value, type: current?.type || "string",
    environment, service, updatedBy: userId,
    description: `Rolled back to version ${targetVersion}`,
  });
}

function encrypt(plaintext: string): string {
  const iv = randomBytes(16);
  const cipher = createCipheriv("aes-256-gcm", ENCRYPTION_KEY, iv);
  const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
  const tag = cipher.getAuthTag();
  return iv.toString("hex") + ":" + encrypted.toString("hex") + ":" + tag.toString("hex");
}

function decrypt(ciphertext: string): string {
  const [ivHex, encHex, tagHex] = ciphertext.split(":");
  const decipher = createDecipheriv("aes-256-gcm", ENCRYPTION_KEY, Buffer.from(ivHex, "hex"));
  decipher.setAuthTag(Buffer.from(tagHex, "hex"));
  return decipher.update(Buffer.from(encHex, "hex")) + decipher.final("utf8");
}

function validateValue(value: any, rule: string): boolean {
  try {
    const regex = new RegExp(rule);
    return regex.test(String(value));
  } catch {
    return true;
  }
}

// Get change audit trail
export async function getAuditTrail(
  key: string, environment?: string
): Promise<Array<{ version: number; oldValue: string; newValue: string; changedBy: string; changedAt: string }>> {
  let sql = "SELECT * FROM config_history WHERE key = $1";
  const params: any[] = [key];
  if (environment) { sql += " AND environment = $2"; params.push(environment); }
  sql += " ORDER BY version DESC LIMIT 50";
  const { rows } = await pool.query(sql, params);
  return rows;
}

Results

  • One source of truth — all 15 services read config from one place; no more scattered .env files and hardcoded values
  • Hot-reload without redeploy — change feature flag, services pick it up in 30 seconds via short cache TTL + pub/sub notification; no rolling restart needed
  • Secrets encrypted at rest — AES-256-GCM encryption; database breach doesn't expose API keys; secrets decrypted only in memory at read time
  • Wrong Redis URL incident prevented — validation rules catch invalid URLs before save; environment-specific overrides mean staging changes can't affect production
  • Full audit trail — every config change logged with who, when, old value, new value; rollback to any previous version in one call

Related use cases