Ensuring type safety and cache consistency with SHA-1 hashing
FlagFlow implements a sophisticated hash-based validation system that ensures your client-side TypeScript types remain in sync with server-side flag schemas. This prevents runtime errors and provides automatic cache invalidation when flag schemas change.
Core Concept: Each flag group has a unique SHA-1 hash based on its structure. When schemas change, hashes change, automatically invalidating outdated client code.
FlagFlow generates SHA-1 hashes for each flag group using a combination of:
Flags are organized hierarchically using forward slashes as separators. Each level gets its own hash:
// Example flag structure and their hashes accounting/ -> HASH_FLAGFLOW__ACCOUNTING accounting/huf/ -> HASH_FLAGFLOW__ACCOUNTING__HUF accounting/huf/allow_exchange -> Individual flag in group // Generated hash constants export const HASH_FLAGFLOW = '2a3e205e597f33fd493563b6db4543a40e1ec986'; export const HASH_FLAGFLOW__ACCOUNTING = 'a004c5733c25be5995e918335aa777b1c5c0d4f4'; export const HASH_FLAGFLOW__ACCOUNTING__HUF = 'b12f456a789c012d345e678f901a234b567c890d';
The validation process uses HTTP headers for hash exchange:
| Header | Direction | Purpose |
|---|---|---|
| x-accept-flaggroup-hash | Client → Server | Expected hash from client's perspective |
| x-flaggroup-hash | Server → Client | Current hash from server's perspective |
When you generate TypeScript definitions from FlagFlow, hash constants are automatically included for use in your client applications:
import axios from "axios";
import { flagFlow_Descriptors, FlagFlow_DescriptorTypeMap } from "./flagflowTypes";
const FLAGFLOW_BASE_URL = 'http://localhost:3000/flags';
export const fetchData = async <K extends keyof FlagFlow_DescriptorTypeMap>(
key: K
): Promise<FlagFlow_DescriptorTypeMap[K]> => {
const { uri, hash } = flagFlow_Descriptors[key];
const { data } = await axios.get<FlagFlow_DescriptorTypeMap[K]>(
FLAGFLOW_BASE_URL + uri, {
responseType: 'json',
headers: {
'Content-Type': 'application/json',
'x-accept-flaggroup-hash': hash, // <- Hash validation
}
})
return data
}The generated TypeScript includes a descriptor map that connects flag groups to their URIs and hashes:
export const flagFlow_Descriptors = {
'#root': {
uri: '/',
hash: HASH_FLAGFLOW
},
'accounting': {
uri: '/accounting',
hash: HASH_FLAGFLOW__ACCOUNTING
},
'accounting/huf': {
uri: '/accounting/huf',
hash: HASH_FLAGFLOW__ACCOUNTING__HUF
}
} as const;When hashes don't match, FlagFlow returns a 409 Conflict status with detailed information:
// Server response when hashes don't match
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": "Hash mismatch detected",
"details": {
"expected": "2a3e205e597f33fd493563b6db4543a40e1ec986",
"actual": "3b4f306f608g44ge504674c7ec5654b51f2fd097",
"group": "accounting/huf",
"message": "Flag schema has changed. Please update your TypeScript definitions."
}
}export const fetchDataWithErrorHandling = async <K extends keyof FlagFlow_DescriptorTypeMap>(
key: K
): Promise<FlagFlow_DescriptorTypeMap[K]> => {
try {
return await fetchData(key);
} catch (error) {
if (error.response?.status === 409) {
console.error('🚨 Schema hash mismatch!');
console.error('Please regenerate your TypeScript definitions:');
console.error('curl http://localhost:3000/type/typescript > flagflowTypes.ts');
throw new Error('Flag schema out of date. Please update TypeScript definitions.');
}
throw error;
}
}FlagFlow provides dedicated endpoints for working with hashes:
| Endpoint | Description | Response |
|---|---|---|
| GET /type/hash | Get hash map for all flag groups | JSON with group → hash mapping |
| GET /flags/[group] | Get flag values with hash validation | Flag data or 409 if hash mismatch |
// Get current hash map
curl http://localhost:3000/type/hash
// Response
{
"#root": "2a3e205e597f33fd493563b6db4543a40e1ec986",
"accounting": "a004c5733c25be5995e918335aa777b1c5c0d4f4",
"accounting/huf": "b12f456a789c012d345e678f901a234b567c890d"
}#!/bin/bash
# update-flagflow-types.sh
echo "🔄 Checking for FlagFlow schema changes..."
# Download current TypeScript definitions
curl -s http://localhost:3000/type/typescript > flagflowTypes.new.ts
# Compare with existing file
if ! cmp -s flagflowTypes.ts flagflowTypes.new.ts; then
echo "📝 Schema changes detected, updating types..."
mv flagflowTypes.new.ts flagflowTypes.ts
echo "✅ TypeScript definitions updated!"
else
echo "✅ No schema changes detected."
rm flagflowTypes.new.ts
fi# In your CI pipeline (e.g., GitHub Actions)
name: Check FlagFlow Schema
on: [push, pull_request]
jobs:
check-flagflow:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check FlagFlow schema sync
run: |
# Download current schema
curl $FLAGFLOW_URL/type/typescript > current-schema.ts
# Compare with committed version
if ! cmp -s flagflowTypes.ts current-schema.ts; then
echo "❌ FlagFlow schema out of sync!"
echo "Run: curl $FLAGFLOW_URL/type/typescript > flagflowTypes.ts"
exit 1
fi
echo "✅ FlagFlow schema is up to date"/type/typescript