OpenParable
Skip to content
← Back to skills

Edge Config

DevOps & Security
edge-config

Edge Config is an Agent Skill for devops & security published in the useautumn/autumn repository on GitHub: a SKILL.md instruction file an AI agent can load to do this task.

Understand and create S3-backed edge configs with poll-based caching. Use when adding new runtime configs (feature rollouts, request blocking, operational toggles), debugging edge config polling, or working with the EdgeConfigStore factory.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/useautumn/autumn/blob/HEAD/.agents/skills/edge-config/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/edge-config, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

Edge Configs

Edge configs are JSON files stored in S3 that are polled into server memory at a regular cadence. They let us change server behavior at runtime without deploys -- load shedding, feature rollouts, operational toggles, etc.

Architecture

Each edge config has its own S3 file under the admin/ prefix in the admin S3 bucket (autumn-dev-server / autumn-prod-server). Configs are independent -- a parse error in one does not affect another.

Key files

FilePurpose
server/src/internal/misc/edgeConfig/edgeConfigStore.tscreateEdgeConfigStore<T> factory -- generic S3 read/write, polling, status tracking
server/src/internal/misc/edgeConfig/edgeConfigRegistry.tsRegistry -- registerEdgeConfig, startAllEdgeConfigPolling, stopAllEdgeConfigPolling
server/src/init.tsCalls startAllEdgeConfigPolling on boot, stopAllEdgeConfigPolling on shutdown
server/src/external/aws/s3/adminS3Config.tsBucket/region resolution (dev vs prod)

Existing configs

ConfigS3 KeyModule
Request blocksadmin/request-block-config.jsonserver/src/internal/misc/requestBlocks/requestBlockStore.ts

Lifecycle

Boot:
  1. Config module is imported (top-level side effect) -> calls registerEdgeConfig()
  2. init.ts calls startAllEdgeConfigPolling({ logger })
  3. Each store: await refresh() (initial S3 fetch, blocks until done)
  4. Each store: setInterval(refresh, pollIntervalMs)
  5. Server starts accepting traffic

Runtime:
  - get() reads from in-memory cache (zero I/O, sync)
  - refresh() runs every pollIntervalMs (fire-and-forget)
  - writeToSource() writes to S3 + updates local cache immediately

Shutdown:
  - stopAllEdgeConfigPolling() clears all intervals

Fail-Open Guarantee

On any S3 failure (network error, auth error, bad JSON, schema mismatch), runtimeConfig resets to defaultValue(). This means:

  • No request is ever blocked due to infrastructure failure
  • If S3 goes down, configs degrade to their default (empty/safe) state
  • The store does NOT hold onto stale data -- it resets to default

NoSuchKey (file doesn't exist yet) is treated as a normal empty state, not an error.

How to Add a New Edge Config

1. Define the schema

Create a schemas file with a Zod schema:

// server/src/internal/misc/featureRollouts/featureRolloutSchemas.ts
import { z } from "zod/v4";

export const FeatureRolloutConfigSchema = z.object({
  features: z.record(z.string(), z.object({
    percentage: z.number().min(0).max(100).default(0),
    enabled: z.boolean().default(false),
  })).default({}),
});

export type FeatureRolloutConfig = z.infer<typeof FeatureRolloutConfigSchema>;

2. Create the store module

// server/src/internal/misc/featureRollouts/featureRolloutStore.ts
import { createEdgeConfigStore } from "@/internal/misc/edgeConfig/edgeConfigStore.js";
import { registerEdgeConfig } from "@/internal/misc/edgeConfig/edgeConfigRegistry.js";
import {
  type FeatureRolloutConfig,
  FeatureRolloutConfigSchema,
} from "./featureRolloutSchemas.js";

const store = createEdgeConfigStore<FeatureRolloutConfig>({
  s3Key: "admin/feature-rollout-config.json",
  schema: FeatureRolloutConfigSchema,
  defaultValue: () => ({ features: {} }),
  pollIntervalMs: 300_000, // 5 minutes
});

registerEdgeConfig({ store });

export const getFeatureRolloutConfig = () => store.get();

export const isFeatureEnabled = ({
  featureId,
}: {
  featureId: string;
}): boolean => {
  const feature = store.get().features[featureId];
  return feature?.enabled ?? false;
};

3. Register the import in init.ts

Add a side-effect import so the module runs registerEdgeConfig at boot:

// server/src/init.ts
import "./internal/misc/featureRollouts/featureRolloutStore.js";

4. (Optional) Add the S3 key constant

// server/src/external/aws/s3/adminS3Config.ts
export const ADMIN_FEATURE_ROLLOUT_CONFIG_KEY = "admin/feature-rollout-config.json";

5. Use it

import { isFeatureEnabled } from "@/internal/misc/featureRollouts/featureRolloutStore.js";

if (isFeatureEnabled({ featureId: "new-billing-engine" })) {
  // new path
}

Factory API Reference

const store = createEdgeConfigStore<T>({
  s3Key: string,              // S3 object key under admin bucket
  schema: z.ZodType<T>,       // Zod schema for validation
  defaultValue: () => T,      // Factory for empty/safe default
  pollIntervalMs?: number,    // Poll interval (default: 60_000)
  s3Client?: S3Client,        // Optional DI for testing
});

store.get()                   // T -- in-memory cached config (sync, no I/O)
store.getStatus()             // EdgeConfigStatus -- health/timing info
store.refresh({ logger? })    // Re-fetch from S3, update cache
store.startPolling({ logger? })  // Initial fetch + start interval
store.stopPolling()           // Clear interval
store.readFromSource()        // Direct S3 read (bypasses cache)
store.writeToSource({ config })  // S3 write + update cache immediately

Testing

Unit tests use the s3Client DI parameter to inject a mock:

const mockClient = { send: jest.fn(async () => ({ Body: ... })) } as unknown as S3Client;

const store = createEdgeConfigStore<MyConfig>({
  s3Key: "admin/test.json",
  schema: MyConfigSchema,
  defaultValue: () => ({ ... }),
  s3Client: mockClient,
});

Existing tests: server/tests/unit/edge-config/edge-config-store.test.ts