BetterIAM

Contributing

Set up the Better IAM repository, find your way around the pnpm workspace, run the build, tests, and conformance checks, and regenerate the docs.

Better IAM is developed in one repository, github.com/Better-IAM/better-iam: a pnpm workspace of TypeScript packages, the reference console, this documentation site, runnable examples, and the test suite. This page covers what you need to change the code: the layout, the commands, how the tests and the documentation are generated and checked, and what CI runs before a release. Every package is licensed under the Apache License 2.0.

Prerequisites

ToolVersionWhy
Node.js22.12 or newer (engines in the root package.json)Runtime for every package, test, and script. CI tests Node 22 and 24.
pnpm11.19.0 (packageManager in the root package.json)Workspace installs and scripts.
A C/C++ toolchainonly if no prebuilt binary fits your platformargon2 and better-sqlite3 are native modules that install prebuilt binaries for common platforms and fall back to compiling with node-gyp. pnpm-workspace.yaml allows their install scripts (allowBuilds).
PostgreSQLany recent versionOnly for the PostgreSQL test suite. CI uses postgres:17-alpine.
Rust (stable) and Go 1.24rust-toolchain.toml, go/go.modOnly for work on the Rust and Go ports.

pnpm is also a dev dependency of the workspace, so node node_modules/pnpm/bin/pnpm.cjs <command> works where pnpm is not on your PATH.

Set up the repository

Clone and install

git clone https://github.com/Better-IAM/better-iam.git
cd better-iam
pnpm install

Build the packages

pnpm build

pnpm build runs tsc -b over the project references, writing each package's dist/. The CLI, the examples, the spec generators, and the docs generator import the built output, so build before running them.

Run the tests

pnpm test

pnpm test runs vitest run over tests/**/*.test.ts and tests/**/*.test.tsx. The Vitest configuration aliases every @better-iam/* package to its src/ entry, so the tests run against source and do not need a build. Files run one at a time (fileParallelism: false) with a 30-second timeout per test.

Run the full check

pnpm check

pnpm check is what CI runs on every push: pnpm typecheck, then pnpm test, then pnpm validate:packages. Run it before you open a pull request.

Repository layout

core: browser-safe types, policy engine, audit chain, storage contract
auth: authentication service (passwords, passkeys, MFA, sessions, outbox)
server: betterIam(), the API groups (src/api), HTTP handler, options
adapter-postgres, adapter-sqlite, adapter-libsql: storage adapters
client: typed browser client
react, vue, svelte, next, nuxt, nestjs, react-router, middleware: framework bindings
oauth, saml, scim, ldap, mcp, a2a: protocol packages
cli: the better-iam command line
projects: the reference plugin
better-iam: the umbrella package with subpath exports

pnpm-workspace.yaml includes packages/*, examples/*, and apps/*. Each directory under packages/ is one published npm package (@better-iam/<name>, plus the umbrella better-iam); Packages lists them with their subpath exports. Server API groups live in packages/server/src/api/<group>.ts, and every betterIam() option is declared in packages/server/src/options.ts (see Options).

Workspace scripts

Every script below is defined in the root package.json and runs from the repository root.

CommandWhat it runs
pnpm buildtsc -b: builds every package's dist/.
pnpm typechecktsc -b plus the type tests in tests/tsconfig.*.json (client, application, Next.js, NestJS, Svelte, middleware, React Router) and the console's typecheck.
pnpm testvitest run: the whole test suite.
pnpm test:postgresThe PostgreSQL suites: tests/postgres.test.ts, tests/server-security.test.ts, tests/adapter-conformance.test.ts, and tests/storage-query.test.ts.
pnpm checkpnpm typecheck && pnpm test && pnpm validate:packages.
pnpm validate:packagesnode scripts/validate-packages.mjs: checks the package manifests.
pnpm pack:allnode scripts/pack.mjs: packs every publishable package.
pnpm check:packed-typesnode scripts/check-packed-types.mjs: checks the types of the packed packages.
pnpm test:packednode scripts/packed-smoke.mjs: a smoke test against the packed packages.
pnpm bench:scalevitest run --config vitest.bench.config.ts: scale benchmarks.
pnpm format / pnpm format:checkPrettier write or check (singleQuote, printWidth 100, trailingComma: 'all').
pnpm docs:apinode scripts/api-reference.mjs.
pnpm spec / pnpm spec:checkRegenerate spec/, or regenerate into a temporary directory and fail on any difference.
pnpm conformance:recordRecord conformance traces from the test suite.
pnpm conformance:serveStart the standalone TypeScript server with the test-control endpoints.
pnpm conformance:replayReplay recorded traces against a server.
pnpm conformance:baselineWrite spec/conformance/BASELINE.md from a replay report.

PostgreSQL tests

The PostgreSQL suites skip themselves unless BETTER_IAM_POSTGRES_URL is set:

export BETTER_IAM_POSTGRES_URL=postgres://postgres:password@localhost:5432/better_iam_test
pnpm build
pnpm test:postgres

The console

pnpm --filter @better-iam/console migrate
pnpm --filter @better-iam/console bootstrap   # reads BETTER_IAM_ROOT_EMAIL and BETTER_IAM_ROOT_PASSWORD
pnpm --filter @better-iam/console dev

Storage adapter conformance

Every storage adapter must pass the same conformance suite, exported from @better-iam/core/conformance. runAdapterConformance(createStore) runs every case against a fresh store (migrating it first and closing it after) and returns { passed, failed } instead of stopping at the first failure; adapterConformanceCases() returns the cases for test runners that want one test per case. The repository runs it for the bundled adapters in tests/adapter-conformance.test.ts.

tests/my-adapter.test.ts
import { expect, test } from 'vitest';
import { runAdapterConformance } from '@better-iam/core/conformance';
import { myAdapter } from '../src/index.js';

test('my adapter passes the storage conformance suite', async () => {
  const { failed } = await runAdapterConformance(() => myAdapter({ url: ':memory:' }));
  expect(failed).toEqual([]);
});

myAdapter stands for your adapter's factory. See Write a database adapter and Adapters and plugins.

Documentation site

The site lives in apps/docs (package @better-iam/docs). Hand-written pages are MDX files in apps/docs/content/docs. Reference data is generated from the repository by apps/docs/scripts/generate.mjs.

pnpm build                                   # the generator reads packages/*/dist
pnpm --filter @better-iam/docs generate      # API, errors, CLI, packages, exports, changelog, glossary, OpenAPI
pnpm --filter @better-iam/docs dev           # http://localhost:4000
node apps/docs/scripts/check-content.mjs     # MDX syntax, components, frontmatter, links, and #anchors
Generator stepOutput
apigenerated/api.json: every API group and method with its route and JSON Schemas, read from a live in-memory instance and the built declarations.
errorsgenerated/errors.json: every IamError code thrown in packages/*/src.
cligenerated/cli.json: every CLI command from its help text.
packages, exportsgenerated/packages.json and generated/exports.json.
changelogcontent/docs/reference/changelog.md from CHANGELOG.md.
api-pages, errors-page, cli-page, exports-page, glossaryThe generated reference pages.
openapipublic/openapi.json.

Pass step names to run only some of them, with an optional filter: node scripts/generate.mjs api-pages=groups,roles errors-page. Set DOCS_COVERAGE=1 to list functions, codes, and commands that still lack a curated description.

Rules for documentation changes:

  • Do not edit generated pages (content/docs/reference/api/*, reference/cli.mdx, reference/errors.mdx, reference/exports.mdx, reference/glossary.mdx, reference/changelog.md). Change the source, or the curated text in apps/docs/descriptions/ (api/{group}.md, cli.md, errors.md, exports.md, glossary.json, fields.json), and regenerate.
  • Every fact on a page (package, import path, method, option, error code, CLI command) must exist in packages/*/src or apps/docs/generated/*.json. List the files you wrote from in the page's sources frontmatter.
  • Each folder's meta.json sets the sidebar title, icon, and page order; a page appears in the sidebar only when it is listed there or the list contains "..." (every other page, alphabetically).
  • Run check-content.mjs with a path prefix (node apps/docs/scripts/check-content.mjs guides) to check one section quickly.

apps/docs/README.md holds the full authoring conventions: frontmatter fields, voice, MDX escaping, the global components, and icons.

Rust and Go ports

The repository also contains native Rust (crates/) and Go (go/) implementations, being built toward parity with the TypeScript server; spec/BUILDOUT.md describes the plan and its status. The TypeScript packages remain the single source of truth:

  • spec/ is generated from the TypeScript sources by pnpm spec (after pnpm build): the OpenAPI document and route table, error codes, the permission catalog, the storage model and migrations, and crypto and format test vectors. Never edit a generated file in spec/ by hand; change the TypeScript and regenerate. pnpm spec:check fails when the checked-in files are out of date.
  • pnpm conformance:record runs the Vitest suite with BETTER_IAM_RECORD set, recording API traces into spec/conformance/traces. pnpm conformance:replay replays them over HTTP against any server that implements the conformance flags and control endpoints, the TypeScript one included (pnpm conformance:serve).
  • crates/PORTING.md explains how to port a server module to Rust.

CI runs cargo fmt --check, cargo clippy, and cargo test for the Rust workspace, and gofmt, go vet, and go test for the Go module, each on Linux, Windows, and macOS, plus a PostgreSQL job for each.

Continuous integration and releases

.github/workflows/ci.yml runs on every push to main and every pull request:

JobSteps
test (Ubuntu and Windows, Node 22 and 24)pnpm install --frozen-lockfile, pnpm check, node examples/shared/smoke.mjs, pnpm pack:all, pnpm check:packed-types, node scripts/packed-smoke.mjs
postgrespnpm build, then pnpm test:postgres against a PostgreSQL 17 service
rust, rust-postgres, go, go-postgresThe Rust and Go checks above

After every green push to main, the release jobs publish any version that is not yet published: npm packages, crates on crates.io, and a Go module tag, all at one shared version. The versions must match, so bump them together with:

node scripts/release-version.mjs 0.4.0

The script rewrites the version of every packages/*/package.json, the Rust workspace version in Cargo.toml, and the Version constant in go/version.go. Then update CHANGELOG.md (newest version first) and run pnpm install, pnpm check, cargo test --workspace, and go test ./... (in go/), as the script reminds you.

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page