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

tenant-evidence-kit

v0.1.3

Published

A TypeScript toolkit for private, multi-tenant evidence workflows with Supabase.

Readme

Tenant Evidence Kit

A small TypeScript toolkit for private, multi-tenant evidence workflows with Supabase.

npm: tenant-evidence-kit · latest: 0.1.2

It provides a reference architecture for a common problem: an application needs to attach photos, documents, or other evidence to a business object without making files public, leaking tenant data, or duplicating authorization rules across the UI and storage layer.

What it does

  • scopes every object by tenant → subject → evidence object;
  • uploads files to a private Supabase Storage bucket;
  • stores evidence metadata separately from file bytes;
  • uses Row Level Security (RLS) for tenant isolation;
  • creates short-lived signed URLs instead of permanent public links;
  • compensates for a metadata failure by removing the uploaded object;
  • ships a reference SQL migration for tenant bootstrap, membership, evidence metadata, and Storage policies;
  • stays intentionally small: it does not impose a CRM, healthcare, rental, document, or workflow domain model.

Why this exists

Private evidence is easy to demo and surprisingly easy to get wrong in a real multi-tenant product. A public bucket, a tenant id trusted only by the client, or metadata stored in two places can create avoidable privacy and consistency problems.

Tenant Evidence Kit keeps the reusable infrastructure separate from the product that consumes it.

Status

v0.1.2 — early public release.

The core path, upload, metadata, listing, signed-access flow, reference RLS model, and tests are present. The API may still evolve before 1.0.

Requirements

  • Node.js 22.12+
  • TypeScript 5+
  • Supabase JS 2.x
  • a Supabase project with Auth, Postgres, and Storage

Quick start

1. Apply the reference migration

Run:

supabase/migrations/0001_tenant_evidence.sql

The migration creates:

tek_tenants
tek_tenant_memberships
tek_evidence
tenant-evidence-private (private Storage bucket)

It also creates the tek_create_tenant() RPC and RLS policies that verify an active tenant role and an explicit operation permission for database rows and Storage objects.

2. Install

Install the published package from npm:

npm install tenant-evidence-kit

For local development of this repository:

npm install
npm run check

3. Create the client and kit

import { createClient } from "@supabase/supabase-js";
import { createTenantEvidenceKit } from "tenant-evidence-kit";

const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);
const evidence = createTenantEvidenceKit(supabase);

Use the publishable/anon client key, not a service-role key in browser code. RLS is the security boundary.

4. Bootstrap a tenant

An authenticated user can create a tenant and become its first owner through the reference RPC:

const { data: tenantId, error } = await supabase.rpc("tek_create_tenant", {
  tenant_name: "Example Company",
});

5. Upload private evidence

const record = await evidence.uploadEvidence({
  tenantId,
  subjectId: operationId,
  body: file,
  fileName: "after-service.jpg",
  contentType: "image/jpeg",
  kind: "photo",
  note: "Captured after completion",
});

The object path is deterministic in shape but opaque at the object level:

{tenantId}/{subjectId}/{objectId}-{sanitizedFileName}

The file remains private. The metadata row contains the reference; it does not duplicate the file.

6. Request temporary access

const { url } = await evidence.createSignedEvidenceUrl({
  filePath: record.filePath,
  expiresInSeconds: 120,
});

Signed URLs are intentionally short-lived. The reference default is 5 minutes.

7. List evidence for a subject

const records = await evidence.listEvidence({
  tenantId,
  subjectId: operationId,
});

Security model

The reference implementation follows five rules:

  1. The bucket is private. No permanent public URL is generated.
  2. Permissions are checked server-side. Storage policies derive the tenant from the first object-path segment and require the authenticated user's active role to grant the requested operation.
  3. Metadata is protected by RLS. A client-provided tenantId alone never grants access.
  4. Service-role credentials stay server-side. Normal application flows are expected to use authenticated Supabase clients.
  5. Evidence is append-only. Metadata can be created, read, or deliberately deleted, but it cannot be updated in place.

Reference roles and permissions

The bundled schema assigns sensitive deletion to owner and admin. Other active membership roles retain the existing read/create behavior for backward compatibility; unknown permission names fail closed.

| Role | Read evidence | Create evidence | Delete evidence | | --- | --- | --- | --- | | owner | yes | yes | yes | | admin | yes | yes | yes | | member (or a custom active role) | yes | yes | no |

Deletion is the sensitive operation reserved for owners and admins. Active members retain read and creation access, preserving the behavior of earlier releases. The metadata and Storage policies use the same permission for each operation, so an ordinary member cannot delete either side independently. Changing a record is intentionally not a permission at all. If facts need correcting, the consuming product should preserve the original evidence and append a new record or use an application-specific supersession model.

See SECURITY.md before using the reference migration in production.

Consistency across Storage and Postgres

Supabase Storage and Postgres do not participate in one shared database transaction. The upload flow therefore uses a compensation strategy:

upload private object
        ↓
insert protected metadata
        ↓
metadata fails? → attempt object cleanup → return typed error

This is deliberately described as compensated consistency, not as a false cross-service atomic transaction.

Release security

Package releases are published from GitHub Actions through npm Trusted Publishing (OIDC). The repository does not require a long-lived npm publish token for the release workflow.

Before publishing, the workflow:

  • installs dependencies;
  • runs typecheck, tests, and build;
  • verifies that the GitHub release tag matches the version in package.json;
  • publishes to npm only after those checks pass.

What this project does not do

Tenant Evidence Kit does not provide:

  • business-specific permissions;
  • clinical or legal record semantics;
  • document signing;
  • image processing;
  • retention-policy automation;
  • virus scanning;
  • a full tenant administration UI.

Those concerns should remain in the consuming product or be added as optional adapters when there is evidence they belong here.

Project structure

src/
  errors.ts
  kit.ts
  path.ts
  types.ts
  index.ts
supabase/
  migrations/
    0001_tenant_evidence.sql
    0002_authorization_hardening.sql
  tests/
    authorization.test.sql
tests/
  path.test.ts
examples/
  basic.ts

Development

npm install
npm run typecheck
npm test
npm run build

Or run everything:

npm run check

Roadmap

The immediate roadmap is intentionally narrow:

  • local-Supabase integration tests for RLS and Storage policies;
  • bring-your-own tenant/membership authorization adapter;
  • safe delete workflow with compensation and audit metadata;
  • framework examples after the core API stabilizes.

Roadmap work is tracked in GitHub Issues.

Contributing

Issues and focused pull requests are welcome. See CONTRIBUTING.md.

License

MIT. See LICENSE.