Keysmith

Multi-Tenancy

Context-based tenant isolation across all subsystems.

Keysmith is multi-tenant by design. Every operation is scoped to an app and tenant via context.Context. Cross-tenant data access is structurally impossible.

Setting tenant context

Standalone mode

Use keysmith.WithTenant to inject app ID and tenant ID into the context.

ctx := keysmith.WithTenant(ctx, "my-app", "tenant-1")

Forge mode

When running as a Forge extension, Keysmith automatically extracts scope from forge.Scope:

// Forge injects scope automatically via middleware.
// AppID comes from forge.ScopeOrganization
// TenantID comes from forge.ScopeOrganization

How isolation works

LayerMechanism
ContextWithTenant injects app ID and tenant ID into context.Context
EngineEvery operation reads scope from context before querying
StoreAll queries filter by app ID and tenant ID
ValidationKey validation verifies the key belongs to the requesting tenant
Plugin hooksLifecycle events carry tenant context for audit and metrics

Reading scope from context

appID := keysmith.AppIDFromContext(ctx)
tenantID := keysmith.TenantIDFromContext(ctx)

Store-level isolation

Every store method receives the app ID and tenant ID from context. Queries are always scoped:

-- Example: listing keys for a tenant
SELECT * FROM keysmith_keys
WHERE app_id = $1 AND tenant_id = $2
ORDER BY created_at DESC

A key created by tenant A is never returned when tenant B queries.

Key validation across tenants

When a raw API key is validated, the engine:

  1. Hashes the raw key with SHA-256
  2. Looks up the hash in the store (global lookup, not tenant-scoped)
  3. Verifies the found key belongs to the requesting tenant
  4. Returns ErrKeyNotFound if the key belongs to a different tenant

This design allows keys to be globally unique while maintaining strict tenant isolation.

On this page