npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tenence/sdk

v0.2.4

Published

Row-level access control for PostgreSQL. Multi-tenant isolation, built-in auth, and embeddable UI components.

Downloads

447

Readme

@tenence/sdk

Build secure, multi-tenant applications without worrying about data isolation. Tenence handles tenant separation at the database level using PostgreSQL Row Level Security — so you can focus on building your product.

For full documentation, guides, and tutorials, visit tenence.io/docs.

Install

npm install @tenence/sdk

Peer dependencies (install if not already present):

npm install pg
npm install @openfeature/server-sdk  # only if using feature flags

Quick Start

The fastest way to get started is with the guided setup wizard:

npx tenence setup

This walks you through authenticating, selecting a project, connecting your database, installing the RLS stored procedures, and generating a config file — all in one step.

Or, if you prefer to do it step by step:

1. Authenticate

Log in to your Tenence account from the terminal (similar to gh auth login):

npx tenence auth login

A browser window will open asking you to approve the CLI. Once approved, your credentials are stored locally at ~/.config/tenence/auth.json.

2. Set up your database

Install the required PostgreSQL stored procedures:

npx tenence init

3. Connect your project

Register your database with the Tenence console:

npx tenence register

4. Create a client

import { createClient } from "@tenence/sdk";

const tenence = createClient({
  databaseUrl: process.env.DATABASE_URL,
});

5. Add the middleware

The middleware automatically connects each incoming request to the right tenant:

import { tenenceMiddleware } from "@tenence/sdk/middleware";

app.use(tenenceMiddleware({
  client: tenence,
  resolveContext: (req) => ({
    tenantId: req.user.orgId,
    userId: req.user.id,
    role: req.user.role,
  }),
}));

6. Query with automatic tenant isolation

Every database query is automatically scoped to the current tenant — no manual filtering needed:

app.get("/api/tasks", async (req, res) => {
  const result = await req.tenence.query("SELECT * FROM tasks");
  res.json(result.rows);
});

For lower-level control, use withConnection to get a raw pg client with tenant context already set:

app.get("/api/tasks", async (req, res) => {
  const rows = await req.tenence.withConnection(async (client) => {
    const result = await client.query("SELECT * FROM tasks WHERE status = $1", ["active"]);
    return result.rows;
  });
  res.json(rows);
});

CLI

The CLI provides GitHub-style browser-based authentication and tools for managing your Tenence setup.

npx tenence setup          # Guided setup wizard (recommended for new projects)

npx tenence auth login     # Authenticate via browser
npx tenence auth logout    # Clear stored credentials
npx tenence auth status    # Show current auth status

npx tenence init           # Install RLS stored procedures into your database
npx tenence status         # Check if stored procedures are installed
npx tenence register       # Connect your database to the Tenence console

Options:

  • --host=URL — Override the console URL (default: https://console.tenence.io)
  • TENENCE_CONSOLE_URL env var — Alternative way to set the console URL
  • TENENCE_API_KEY env var — Use an API key instead of browser auth

Credentials are stored in ~/.config/tenence/auth.json. Tokens expire after 90 days.

What's Included

Data Isolation

Tenence compiles your access rules into native PostgreSQL RLS policies. Each tenant only sees their own data, enforced at the database level. Learn more in the Concepts Guide.

Authentication

Connect your preferred auth provider — or use the built-in Tenence Auth Gateway. Out-of-the-box adapters for:

  • Tenence Auth Gateway — JWT-based with automatic JWKS verification
  • Clerk — Drop-in adapter
  • Custom providers — Bring your own auth logic
  • Built-in sessions — Password-based auth with Express sessions

Gateway adapter:

import { createGatewayAdapter } from "@tenence/sdk/auth";

const auth = createGatewayAdapter({
  jwksUrl: "https://console.tenence.io/api/gateway/your-project/.well-known/jwks.json",
});

Clerk adapter:

import { createClerkAdapter } from "@tenence/sdk/auth";

const auth = createClerkAdapter();

Custom adapter:

import { createCustomAdapter } from "@tenence/sdk/auth";

const auth = createCustomAdapter({
  resolveUser: async (req) => ({
    userId: req.user.id,
    email: req.user.email,
    tenantId: req.user.orgId,
    role: req.user.role,
  }),
  resolveContext: async (req) => ({
    tenantId: req.user.orgId,
    userId: req.user.id,
    role: req.user.role,
  }),
});

See the Auth Gateway Docs for more details.

Entitlements & Billing

Control access to features based on each tenant's subscription plan. Gate premium features, enforce usage limits, and manage trials — all from the Tenence Console.

import { entitlementMiddleware } from "@tenence/sdk";

app.use("/api/premium", entitlementMiddleware({
  feature: "advanced_analytics",
  mode: "hard_block",
}));

You can also check entitlements programmatically:

import { checkEntitlement, parseEntitlementClaims } from "@tenence/sdk";

const claims = parseEntitlementClaims(req.user.metadata.entitlements);
const result = checkEntitlement(claims, { feature: "advanced_analytics" });

if (!result.allowed) {
  return res.status(403).json({ message: result.reason });
}

Learn more in the Billing Docs.

Feature Flags

Toggle features per tenant using the OpenFeature standard:

import { OpenFeature } from "@openfeature/server-sdk";
import { TenenceProvider } from "@tenence/sdk/feature-flags";

OpenFeature.setProvider(new TenenceProvider({
  apiUrl: "https://console.tenence.io",
  apiKey: process.env.TENENCE_API_KEY,
  projectId: "your-project-id",
}));

const client = OpenFeature.getClient();
const showBeta = await client.getBooleanValue("beta_feature", false, {
  targetingKey: orgId,
});

See the Feature Flags Docs for configuration details.

UI Components

Drop-in Web Components for common multi-tenant UI patterns:

import "@tenence/sdk/components";

| Component | What it does | |-----------|-------------| | <tenence-tenant-switcher> | Lets users switch between organizations | | <tenence-user-badge> | Shows the current user's info | | <tenence-role-indicator> | Displays the user's role | | <tenence-paywall> | Shows or hides content based on subscription | | <tenence-feature-gate> | Shows or hides content based on feature flags | | <tenence-impersonation-bar> | Banner when an admin is impersonating a user | | <tenence-theme-provider> | Applies per-organization branding |

Theming

Load and apply per-organization branding dynamically — logos, colors, favicon, and app name:

import { createTheme } from "@tenence/sdk";

const theme = createTheme({
  baseUrl: "https://console.tenence.io",
  projectId: "your-project-id",
  orgId: "org-id",
  autoApply: true, // sets CSS variables and favicon automatically
});

await theme.load();

// Access theme values
theme.getLogoUrl();      // organization logo URL
theme.getAppName();      // branded app name
theme.getPrimaryColor(); // primary brand color
theme.getAccentColor();  // accent color

CSS custom properties set by the theme:

  • --tenence-primary — Primary color
  • --tenence-accent — Accent color
  • --tenence-bg — Background color

autoApply defaults to true in browser environments and false in SSR/Node.js.

Rule Builder

Define access rules with a type-safe builder instead of writing raw SQL. Common patterns are available as one-line templates:

import { eq, and, sessionVar, templates } from "@tenence/sdk";

// Tenant isolation in one line
const isolation = templates.tenantIsolation();

// Or build custom rules
const rule = and(
  eq("project_id", sessionVar("app.project_id", "uuid")),
  eq("tenant_id", sessionVar("app.tenant_id", "uuid")),
);

Built-in templates:

| Template | What it does | |----------|-------------| | templates.tenantIsolation() | Restricts rows to the current tenant | | templates.projectIsolation() | Restricts rows to the current project | | templates.compoundIsolation() | Project + tenant compound isolation | | templates.ownerOnly() | Only the row creator can access it | | templates.adminBypass() | Admins can access all rows | | templates.softDeleteFilter() | Hides soft-deleted rows |

Additional expression functions: col, neq, gt, gte, lt, lte, isNull, isNotNull, ilike, inList, contains, not, or, defineRule

For the full expression builder reference, see the API Reference.

Session Variables

The client maps context keys to PostgreSQL session variables using SET LOCAL. The defaults are:

| Context Key | PostgreSQL Variable | Description | |-------------|-------------------|-------------| | tenantId | app.tenant_id | Current tenant/organization ID | | userId | auth.uid | Authenticated user ID | | role | auth.role | User's role | | projectId | app.project_id | Current project ID |

You can customize these mappings:

const tenence = createClient({
  databaseUrl: process.env.DATABASE_URL,
  poolSize: 10,
  sessionVars: {
    tenantId: "app.tenant_id",
    userId: "auth.uid",
    role: "auth.role",
    projectId: "app.project_id",
  },
});

Client API

The TenenceClient provides these methods:

| Method | Description | |--------|-------------| | query(sql, params?, context?) | Run a SQL query, optionally with tenant context | | withContext(context, fn) | Execute a function with a tenant-scoped connection | | compileExpression(expression) | Compile a rule expression to SQL | | applyRule(config) | Apply an RLS policy to a table | | dropRule(table, name) | Remove an RLS policy | | enableRls(table) | Enable RLS on a table | | disableRls(table) | Disable RLS on a table | | testRule(table, expression, context?) | Simulate a policy and preview matched rows | | close() | Close the connection pool |

AI-Powered Development

The Tenence Console includes an MCP (Model Context Protocol) server that lets AI coding assistants manage your data access rules through natural language. Works with Replit Agent, Cursor, Windsurf, and Claude Desktop.

See the Replit Extension Docs for setup instructions.

Import Paths

| Import | What's inside | |--------|--------------| | @tenence/sdk | Client, expressions, templates, middleware, entitlements, theming | | @tenence/sdk/middleware | Express middleware (tenenceMiddleware) | | @tenence/sdk/auth | Auth adapters (Gateway, Clerk, Custom) and password utilities | | @tenence/sdk/components | Web Components (tenant switcher, paywall, etc.) | | @tenence/sdk/feature-flags | OpenFeature provider (TenenceProvider) |

Learn More

License

MIT