{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "api-keys",
  "type": "registry:block",
  "title": "API Keys",
  "description": "API key lifecycle, scopes, hashed storage, usage metadata, and management UI.",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://stackfoundry.dev/r/drizzle-postgres.json"
  ],
  "files": [
    {
      "path": "packages/db/src/schema/api-keys.ts",
      "type": "registry:file",
      "target": "packages/db/src/schema/api-keys.ts",
      "content": "import { pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\nexport const apiKeys = pgTable(\"api_keys\", {\n  id: uuid(\"id\").primaryKey().defaultRandom(),\n  ownerId: text(\"owner_id\").notNull(),\n  name: text(\"name\").notNull(),\n  prefix: text(\"prefix\").notNull(),\n  hash: text(\"hash\").notNull(),\n  scopes: text(\"scopes\").array().notNull().default([]),\n  lastUsedAt: timestamp(\"last_used_at\", { withTimezone: true }),\n  revokedAt: timestamp(\"revoked_at\", { withTimezone: true }),\n  createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n"
    },
    {
      "path": "apps/web/src/lib/api-keys.ts",
      "type": "registry:file",
      "target": "apps/web/src/lib/api-keys.ts",
      "content": "import \"server-only\";\n\nimport { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nconst KEY_PREFIX = \"sf\";\n\nexport function createApiKeySecret() {\n  const secret = randomBytes(32).toString(\"base64url\");\n  return `${KEY_PREFIX}_${secret}`;\n}\n\nexport function getApiKeyPrefix(secret: string) {\n  return secret.slice(0, 12);\n}\n\nexport function hashApiKey(secret: string) {\n  return createHash(\"sha256\").update(secret).digest(\"hex\");\n}\n\nexport function safeCompareHash(secret: string, expectedHash: string) {\n  const actual = Buffer.from(hashApiKey(secret));\n  const expected = Buffer.from(expectedHash);\n  return actual.length === expected.length && timingSafeEqual(actual, expected);\n}\n"
    },
    {
      "path": "apps/web/src/app/(console)/api-keys/page.tsx",
      "type": "registry:page",
      "target": "apps/web/src/app/(console)/api-keys/page.tsx",
      "content": "export default function ApiKeysPage() {\n  return (\n    <main className=\"flex flex-col gap-4 p-6\">\n      <div>\n        <h1 className=\"text-2xl font-semibold\">API Keys</h1>\n        <p className=\"text-muted-foreground\">Create, scope, rotate, and revoke API keys.</p>\n      </div>\n    </main>\n  );\n}\n"
    }
  ],
  "maintenanceSkills": [
    {
      "name": "api-keys",
      "target": ".stackfoundry/skills/api-keys/SKILL.md",
      "content": "---\nname: api-keys\ndescription: Maintain the API keys module installed by StackFoundry.\n---\n\n# API Keys Operating Instructions\n\n- Never store plaintext keys.\n- Store key prefixes only for display.\n- Hash key secrets with a strong one-way hash.\n- Scope every key to a user, account, or organization.\n- Log create, rotate, revoke, and failed verification events.\n- When adding scopes, update docs, tests, and UI labels together.\n\n## Shared Skills\n\nWhen provider, framework, or database behavior changes, load the installed shared skill before editing implementation details:\n\n- `.stackfoundry/skills/drizzle/SKILL.md` (source: `registry/skills/drizzle/SKILL.md`)\n- `.stackfoundry/skills/nextjs/SKILL.md` (source: `registry/skills/nextjs/SKILL.md`)\n\nKeep this module skill focused on ownership, installed files, env vars, deployment checks, and module-specific invariants.\n\n"
    },
    {
      "name": "drizzle",
      "target": ".stackfoundry/skills/drizzle/SKILL.md",
      "content": "---\nname: drizzle\ndescription: Maintain Drizzle ORM and Postgres code installed by StackFoundry modules.\n---\n\n# Drizzle Operating Instructions\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/drizzle/SKILL.md`\n- Registry source: `registry/skills/drizzle/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep database access in server-only code.\n- Add schema changes under `packages/db/src/schema` and export shared tables from the schema barrel.\n- Generate and commit migrations when schema changes are intended.\n- Use typed query helpers instead of raw SQL unless the query needs a documented escape hatch.\n- Include tenant, organization, or user scope in queries and cache tags whenever data is not global.\n"
    },
    {
      "name": "nextjs",
      "target": ".stackfoundry/skills/nextjs/SKILL.md",
      "content": "---\nname: nextjs\ndescription: Maintain Next.js App Router code installed by StackFoundry modules.\n---\n\n# Next.js Operating Instructions\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/nextjs/SKILL.md`\n- Registry source: `registry/skills/nextjs/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep server-only data access out of Client Components.\n- Put route handlers under `app/api` and UI routes under the relevant App Router segment.\n- Prefer Server Components for data loading and add `\"use client\"` only for interactivity.\n- Keep public environment variables prefixed with `NEXT_PUBLIC_`; keep secrets server-only.\n- Re-run typecheck and build after changing route handlers, layouts, or shared app configuration.\n"
    }
  ],
  "envVars": {},
  "docs": "# API Keys Module\n\nAdds API key creation, hashing, scopes, usage metadata, and management UI.\n\nThis is the default source-owned API key path. Use it when the application should own key storage and verification in its database. Add `unkey-api-keys` only when the app chooses Unkey as a managed provider adapter.\n\n## Owns\n\n- API key schema\n- key creation and revocation actions\n- key verification helper\n- usage/last-used metadata\n- audit log events\n\n## Security\n\nNever store plaintext API keys. Store a prefix for display and a secure hash for verification.\n\n## Adapter Options\n\n- `unkey-api-keys` for managed key issuance, verification, metadata, permissions, and roles.\n- `unkey-rate-limits` for managed rate limits alongside or instead of local `rate-limits`.\n",
  "meta": {
    "category": "developer-platform",
    "env": [],
    "status": "ready",
    "maturity": "ready",
    "drizzle": {
      "schemaExports": [
        "apiKeys"
      ],
      "migrationRecommended": true
    },
    "recommendedFor": []
  }
}
