[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Real-Time Permissions System with Zanzibar

Build a Real-Time Permissions System with Zanzibar

Replace hardcoded RBAC with a relationship-based authorization system inspired by Google Zanzibar — supporting nested teams, shared resources, and fine-grained permissions that evaluate in under 5ms.

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

A collaboration platform has outgrown its RBAC system. Roles (admin, editor, viewer) worked with 100 users. Now with 50K users across 500 organizations, the reality is messy: an editor needs admin access to one project but not others. A contractor should see files in one folder but not its parent. Shared documents need per-document permissions. The current system has 200+ custom role variants and a 500-line canAccess() function nobody dares to refactor. Permission bugs are the #1 source of security incidents — 3 data leaks in the past year.

Step 1: Relationship Tuples

typescript
// src/authz/types.ts
import { z } from 'zod';

// Zanzibar-style relationship tuple: user/group has relation to object
export const RelationTuple = z.object({
  object: z.string(),       // "document:report-2024"
  relation: z.string(),     // "editor"
  subject: z.string(),      // "user:alice" or "team:engineering#member"
});

// Authorization model: defines which relations exist and how they inherit
export const AuthzModel = z.object({
  types: z.record(z.string(), z.object({
    relations: z.record(z.string(), z.object({
      directlyAssignable: z.boolean().default(true),
      // Union: this relation includes users from other relations
      union: z.array(z.string()).optional(),
      // Intersection: user must have ALL of these relations
      intersection: z.array(z.string()).optional(),
      // Parent: inherit from parent object's relation
      fromParent: z.object({
        parentRelation: z.string(),
        parentType: z.string(),
        inheritedRelation: z.string(),
      }).optional(),
    })),
  })),
});

// Example model
export const model: z.infer<typeof AuthzModel> = {
  types: {
    organization: {
      relations: {
        owner: { directlyAssignable: true },
        admin: { directlyAssignable: true, union: ['owner'] },
        member: { directlyAssignable: true, union: ['admin'] },
      },
    },
    project: {
      relations: {
        parent_org: { directlyAssignable: true },
        admin: {
          directlyAssignable: true,
          // Org admins are project admins
          fromParent: { parentRelation: 'parent_org', parentType: 'organization', inheritedRelation: 'admin' },
        },
        editor: { directlyAssignable: true, union: ['admin'] },
        viewer: { directlyAssignable: true, union: ['editor'] },
      },
    },
    document: {
      relations: {
        parent_project: { directlyAssignable: true },
        owner: { directlyAssignable: true },
        editor: {
          directlyAssignable: true,
          union: ['owner'],
          fromParent: { parentRelation: 'parent_project', parentType: 'project', inheritedRelation: 'editor' },
        },
        viewer: {
          directlyAssignable: true,
          union: ['editor'],
          fromParent: { parentRelation: 'parent_project', parentType: 'project', inheritedRelation: 'viewer' },
        },
      },
    },
  },
};

Step 2: Permission Check Engine

typescript
// src/authz/checker.ts
import { Pool } from 'pg';
import { Redis } from 'ioredis';

const db = new Pool({ connectionString: process.env.DATABASE_URL });
const redis = new Redis(process.env.REDIS_URL!);

export async function check(
  object: string,
  relation: string,
  subject: string
): Promise<boolean> {
  // Check cache first (5-second TTL)
  const cacheKey = `authz:${object}:${relation}:${subject}`;
  const cached = await redis.get(cacheKey);
  if (cached !== null) return cached === '1';

  const result = await resolveCheck(object, relation, subject, new Set());

  // Cache result
  await redis.setex(cacheKey, 5, result ? '1' : '0');
  return result;
}

async function resolveCheck(
  object: string,
  relation: string,
  subject: string,
  visited: Set<string>
): Promise<boolean> {
  const visitKey = `${object}:${relation}:${subject}`;
  if (visited.has(visitKey)) return false; // prevent cycles
  visited.add(visitKey);

  // Direct check: is there a tuple (object, relation, subject)?
  const { rows } = await db.query(`
    SELECT 1 FROM relation_tuples
    WHERE object = $1 AND relation = $2 AND subject = $3
    LIMIT 1
  `, [object, relation, subject]);

  if (rows.length > 0) return true;

  // Check group membership: subject might be "team:eng#member"
  // If subject is "user:alice", check if alice is a member of any group that has this relation
  const { rows: groupTuples } = await db.query(`
    SELECT rt.subject FROM relation_tuples rt
    WHERE rt.object = $1 AND rt.relation = $2 AND rt.subject LIKE '%#%'
  `, [object, relation]);

  for (const gt of groupTuples) {
    const [groupObj, groupRel] = gt.subject.split('#');
    const isMember = await resolveCheck(groupObj, groupRel, subject, visited);
    if (isMember) return true;
  }

  // Check relation inheritance (union)
  const [objectType] = object.split(':');
  const modelType = model.types[objectType];
  if (!modelType) return false;

  const relationDef = modelType.relations[relation];
  if (!relationDef) return false;

  // Union: check if subject has any of the parent relations
  if (relationDef.union) {
    for (const parentRelation of relationDef.union) {
      const has = await resolveCheck(object, parentRelation, subject, visited);
      if (has) return true;
    }
  }

  // Parent inheritance
  if (relationDef.fromParent) {
    const { parentRelation, parentType, inheritedRelation } = relationDef.fromParent;
    const { rows: parents } = await db.query(`
      SELECT subject FROM relation_tuples WHERE object = $1 AND relation = $2
    `, [object, parentRelation]);

    for (const parent of parents) {
      const hasInherited = await resolveCheck(parent.subject, inheritedRelation, subject, visited);
      if (hasInherited) return true;
    }
  }

  return false;
}

// Import model
import { model } from './types';

Step 3: API and Middleware

typescript
// src/authz/middleware.ts
import { check } from './checker';

export function authorize(objectFn: (c: any) => string, relation: string) {
  return async (c: any, next: any) => {
    const userId = c.get('userId');
    if (!userId) return c.json({ error: 'Unauthorized' }, 401);

    const object = objectFn(c);
    const allowed = await check(object, relation, `user:${userId}`);

    if (!allowed) return c.json({ error: 'Forbidden' }, 403);
    await next();
  };
}

// Usage:
// app.get('/documents/:id', authorize(c => `document:${c.req.param('id')}`, 'viewer'), handler)
// app.put('/documents/:id', authorize(c => `document:${c.req.param('id')}`, 'editor'), handler)

Results

  • Permission bugs: zero security incidents (was 3 data leaks/year)
  • Check latency: <5ms average with caching (was 50ms+ with nested SQL queries)
  • Custom roles eliminated: 200+ role variants replaced by relationship tuples
  • canAccess() function: deleted — replaced by 3 clean primitives (check, write, delete)
  • Nested permissions: contractor sees files in subfolder but not parent — just works
  • Shared documents: per-document sharing without affecting project-level permissions
  • Audit trail: every permission change is a tuple write, fully auditable

Related use cases