Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Api Keys Akshatsinha0

A Bijection api keys component for Bijection.

Bijection source
git clone https://github.com/bijectionhq/api-keys-akshatsinha0.git
cd api-keys-akshatsinha0
git checkout 21ff297298cf673be5324a403c5b4514e74dfcd1

Bijection repository access is required. Request access.

Description

A Bijection api keys component for Bijection.

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available as a reference

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/api-keys-akshatsinha0. Based on akshatsinha0/convex-api-keys. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

A convex api keys component for Convex.

Upstream overview

From the upstream package documentation.

A zero-dependency Convex component for API key management that handles generation, SHA-256 hashing, verification, rate limiting, RBAC, and audit logging entirely within Convex's transactional guarantees. Provides cryptographically secure keys with sliding window rate limits, usage credits with automatic refills, role-based permissions, and real-time analytics without external services or separate databases.

Use cases

From the upstream package documentation.

  • SaaS API tiering - Issue API keys to customers with different rate limits and usage quotas (free, pro, enterprise plans) while tracking consumption in real-time
  • Microservice gateway authorization - Secure internal services with granular RBAC permissions, allowing keys to access only authorized endpoints like billing:read or users:write
  • Compliance-driven key rotation - Automatically rotate API keys every 90 days for PCI/SOC2 requirements with configurable grace periods to prevent service disruption
  • Webhook endpoint protection - Rate limit and authenticate incoming webhooks from external partners while tracking usage patterns and preventing abuse
  • Multi-tenant API management - Isolate API keys by namespace and owner, enabling white-label solutions or departmental separation within the same Convex deployment

How it works

From the upstream package documentation.

The component stores API keys as SHA-256 hashes in Convex tables and provides both direct component access and a class-based SDK. You register it in convex.config.ts using app.use(apiKeys), then call functions like apiKeys.create() to generate base62-encoded keys and apiKeys.verify() for authentication.

Key verification runs atomically within a single Convex mutation, checking rate limits, decrementing usage credits, logging the attempt, and returning validation results. The sliding window rate limiter and RBAC system use helper functions like hasPermission() to check authorization. Usage analytics automatically roll up into hourly and daily buckets for dashboard queries.

All configuration happens through the ApiKeys constructor, including key entropy (keyBytes), default prefixes, namespaces, and log retention. The component supports key rotation with rotate() functions that maintain grace periods, and provides cursor-based pagination for listing keys with getKeysByOwner().

Upstream package FAQ

How does convex-api-keys store API keys securely?

The convex-api-keys component uses SHA-256 hashing to store keys, meaning plaintext keys never persist in the database. Only the hashed version is stored, and the component generates cryptographically secure keys with configurable entropy using base62 encoding.

Can I use this component for SaaS tiered pricing with usage limits?

Yes, convex-api-keys supports usage credits with automatic refill intervals (hourly, daily, weekly, monthly). You can configure different credit amounts for different pricing tiers and the verify mutation automatically decrements credits while enforcing limits.

Does the component work with multi-tenant applications?

Yes, convex-api-keys supports multi-tenancy through namespaces and owner isolation. Each key has an ownerId and optional namespace, allowing you to segregate keys by customer, organization, or environment while maintaining data isolation.

How do I rotate API keys without downtime?

The convex-api-keys component provides key rotation with configurable grace periods. During rotation, both the old and new keys remain valid for the specified grace period, allowing clients to migrate without service interruption.

What analytics and monitoring does this component provide?

The component includes comprehensive audit logging, automatic analytics rollups with time-bucketed queries, usage statistics per key, and top keys ranking by usage. All data is reactive, so dashboards built with Convex queries update in real-time.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Api Keys Akshatsinha0 with Bijection.

Read: https://bijection.com/components/api-keys-akshatsinha0/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from akshatsinha0/convex-api-keys, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

Convex API Keys

A native, zero-dependency Convex component for API key management. Handles key generation, SHA-256 hashing, verification, rate limiting, RBAC, usage credits, and audit logging -- all within Convex's transactional guarantees.

Found a bug? Feature request? File it here.

Why This Component?

Most API key solutions require external services, webhook plumbing, and separate databases. This component runs entirely inside Convex, giving you:

  • Transactional verification -- key lookup, rate limit check, credit decrement, and audit log write all happen in a single atomic mutation
  • Reactive dashboards -- use Convex queries to build real-time key usage dashboards that update automatically
  • Zero infrastructure -- no Redis for rate limiting, no separate analytics pipeline, no webhook endpoints to maintain
  • Component isolation -- all data lives in the component's private tables; your app tables stay clean

Features

  • Cryptographically secure key generation (configurable entropy, base62 encoded)
  • SHA-256 hashed storage (plaintext keys never persist)
  • Sliding window rate limiting with per-key and per-owner shared overrides
  • Usage credits with automatic refill (hourly/daily/weekly/monthly)
  • Role-based access control (RBAC) with permissions and roles
  • Key rotation with configurable grace periods
  • Full audit logging with action type filtering
  • Cursor-based pagination for key listings
  • Real-time reactive queries for dashboards
  • Analytics rollups (hourly + daily) with time-bucketed queries
  • Top keys by usage ranking
  • Multi-tenant support via namespaces and owner isolation
  • Configurable key bytes, log retention, and rollup intervals

Installation

npm install @00akshatsinha00/convex-api-keys

Register the component in your convex/convex.config.ts:

import { defineApp } from "convex/server";
import apiKeys from "@00akshatsinha00/convex-api-keys/convex.config";

const app = defineApp();
app.use(apiKeys, { name: "apiKeys" });
export default app;

Quick Start

Direct component access

import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";

export const createKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await ctx.runMutation(components.apiKeys.lib.create, {
      ownerId: userId,
      name: args.name,
      namespace: "default",
    });
  },
});

export const verifyKey = mutation({
  args: { key: v.string() },
  handler: async (ctx, args) => {
    return await ctx.runMutation(components.apiKeys.lib.verify, {
      key: args.key,
    });
  },
});

Client SDK (class-based)

import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "production",
  defaultPrefix: "sk_live_",
  keyBytes: 32,              // configurable key entropy (default: 32)
  logRetentionDays: 90,      // auto-applied to purgeVerificationLogs
});

export const createKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await apiKeys.create(ctx, { ownerId: userId, name: args.name });
  },
});

export const verifyKey = mutation({
  args: { key: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, { key: args.key });
    // Static class methods for permission checking
    if (result.valid && ApiKeys.hasPermission(result, "write:data")) {
      // authorized
    }
    return result;
  },
});

Real-World Scenarios

Scenario 1: SaaS API with Tiered Plans

You run a SaaS product and want to issue API keys to customers on different pricing tiers. Free users get 100 requests/day, Pro users get 10,000, and Enterprise gets unlimited.

// convex/apiKeyManager.ts
import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "api-v1",
  defaultPrefix: "sk_live_",
});

// Issue a key when a customer signs up or upgrades
export const issueKeyForPlan = mutation({
  args: {
    plan: v.union(v.literal("free"), v.literal("pro"), v.literal("enterprise")),
  },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;

    const planConfig = {
      free:       { remaining: 100,   refill: { amount: 100,   interval: "daily" as const } },
      pro:        { remaining: 10000, refill: { amount: 10000, interval: "daily" as const } },
      enterprise: {},  // no usage cap
    };

    const config = planConfig[args.plan];

    return await apiKeys.create(ctx, {
      ownerId: userId,
      name: `${args.plan}-key`,
      meta: { plan: args.plan },
      ...config,
      ratelimit: { limit: 60, duration: 60000 },  // 60 req/min burst protection for all tiers
    });
  },
});

// Verify on every API request
export const handleApiRequest = mutation({
  args: { key: v.string(), endpoint: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, {
      key: args.key,
      tags: { endpoint: args.endpoint },
    });

    if (!result.valid) {
      return { error: result.code, message: result.message };
    }

    return {
      authorized: true,
      remaining: result.remaining,  // undefined for Enterprise (unlimited)
      plan: result.meta?.plan,
    };
  },
});

// Dashboard query: show customer their usage
export const myKeyUsage = query({
  args: { keyId: v.string() },
  handler: async (ctx, args) => {
    return await apiKeys.getUsageStats(ctx, { keyId: args.keyId });
    // Returns: { total, valid, rateLimited, usageExceeded, expired, revoked, disabled, notFound }
  },
});

Scenario 2: Multi-Service RBAC (Microservice Gateway)

You have several internal services (billing, users, analytics) behind a gateway. Each API key should only access authorized services.

// convex/gateway.ts
import { ApiKeys, hasPermission, hasAllPermissions } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "gateway",
  defaultPrefix: "gw_",
});

// Admin: set up permissions and roles once
export const bootstrapRbac = mutation({
  handler: async (ctx) => {
    // Create granular permissions
    await apiKeys.createPermission(ctx, { name: "billing:read",  description: "View invoices" });
    await apiKeys.createPermission(ctx, { name: "billing:write", description: "Create charges" });
    await apiKeys.createPermission(ctx, { name: "users:read",    description: "List users" });
    await apiKeys.createPermission(ctx, { name: "users:write",   description: "Manage users" });
    await apiKeys.createPermission(ctx, { name: "analytics:read", description: "View metrics" });

    // Bundle permissions into roles
    await apiKeys.createRole(ctx, {
      name: "billing-admin",
      permissions: ["billing:read", "billing:write"],
    });
    await apiKeys.createRole(ctx, {
      name: "readonly",
      permissions: ["billing:read", "users:read", "analytics:read"],
    });
    await apiKeys.createRole(ctx, {
      name: "superadmin",
      permissions: ["billing:read", "billing:write", "users:read", "users:write", "analytics:read"],
    });
  },
});

// Issue a key with specific roles
export const issueServiceKey = mutation({
  args: { serviceName: v.string(), roles: v.array(v.string()) },
  handler: async (ctx, args) => {
    const result = await apiKeys.create(ctx, {
      ownerId: args.serviceName,
      name: `${args.serviceName}-key`,
    });

    // Assign roles to the newly created key
    await apiKeys.assignRoles(ctx, {
      keyId: result.keyId,
      roles: args.roles,
    });

    return result;
  },
});

// Gateway: verify and check permissions per endpoint
export const routeRequest = mutation({
  args: { key: v.string(), service: v.string(), action: v.string() },
  handler: async (ctx, args) => {
    const result = await apiKeys.verify(ctx, { key: args.key });

    if (!result.valid) {
      return { status: 401, error: result.code };
    }

    const requiredPermission = `${args.service}:${args.action}`;
    if (!hasPermission(result, requiredPermission)) {
      return { status: 403, error: "INSUFFICIENT_PERMISSIONS" };
    }

    return { status: 200, keyId: result.keyId, permissions: result.permissions };
  },
});

Scenario 3: Key Rotation for PCI/SOC2 Compliance

Your security policy requires rotating API keys every 90 days. You want zero-downtime rotation with a grace period so clients can migrate.

// convex/compliance.ts
import { ApiKeys, isKeyExpiringSoon, calculateExpiration } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "production",
  defaultPrefix: "sk_live_",
});

// Create key with 90-day expiry
export const createComplianceKey = mutation({
  args: { name: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await apiKeys.create(ctx, {
      ownerId: userId,
      name: args.name,
      expires: calculateExpiration(90),  // 90 days from now
    });
  },
});

// Rotate a key with 48-hour grace period -- both old and new key work during grace
export const rotateKey = mutation({
  args: { keyId: v.string() },
  handler: async (ctx, args) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;

    const key = await apiKeys.getKey(ctx, { keyId: args.keyId });
    if (!key || key.ownerId !== userId) {
      throw new Error("Unauthorized");
    }

    // Old key stays valid for 48 hours; new key is returned immediately
    return await apiKeys.rotate(ctx, {
      keyId: args.keyId,
      gracePeriodMs: 48 * 60 * 60 * 1000,  // 48 hours
    });
  },
});

// Dashboard: show keys that need rotation soon
export const keysNeedingRotation = query({
  args: {},
  handler: async (ctx) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    const keys = await apiKeys.getKeysByOwner(ctx, { ownerId: userId });

    return keys.filter((k) => isKeyExpiringSoon(k.expires, 14));  // expiring within 14 days
  },
});

Scenario 4: Webhook Endpoint Protection

You expose a webhook URL that external services call. You want to rate limit incoming webhooks and track which integration sent each request.

// convex/webhooks.ts
import { ApiKeys } from "@00akshatsinha00/convex-api-keys";
import { components } from "./_generated/api";
import { httpAction, mutation } from "./_generated/server";
import { v } from "convex/values";

const apiKeys = new ApiKeys(components.apiKeys, {
  defaultNamespace: "webhooks",
  defaultPrefix: "whk_",
});

// Issue a webhook key for each integration partner
export const createWebhookKey = mutation({
  args: {
    partnerName: v.string(),
    maxPerMinute: v.number(),
  },
  handler: async (ctx, args) => {
    return await apiKeys.create(ctx, {
      ownerId: args.partnerName,
      name: `${args.partnerName}-webhook`,
      meta: { type: "webhook", partner: args.partnerName },
      ratelimit: { limit: args.maxPerMinute, duration: 60000 },
    });
  },
});

// HTTP action: receive webhook, verify key from header
export const receiveWebhook = httpAction(async (ctx, request) => {
  const authHeader = request.headers.get("

[README truncated for prompt length]