Repository layout

For contributors. The modules are in directories by protocol family.

server.js            the shell: requires the modules and listens
sts_metadata.ts      reads the router to list what everything else registered

common/              config, helpers, the express app, the counters, the audit
                     log, the application registry, the claim catalogues
common/vendored/     byte-identical copies of the parent project's PKI and
                     JSON-LD modules, plus the contexts they read
oauth-oidc/          the authorization server, RFC 9700 mode, DPoP, mTLS,
                     client authentication, the multi-AS profiles
authn/               the sign-in screen and the WebAuthn relying party
saml/                the two assertion builders
ws-trust/            WS-Trust 1.0-1.4
ws-federation/       WS-Federation 1.2 and the mock relying party
kerberos/            the KDC, the acceptor, SPNEGO, and the codec
ldap/                the embedded directory
persistence/         the only place this service writes anything down:
                     memory | ldif (RFC 2849) | postgres
scim/                /scim/v2, its authentication, its attribute mapping
ssf/                 Shared Signals: the transmitter, the RFC 9493 subject
                     grammar, the RFC 8417 envelope, the streams, the gate,
                     and the second outbound request in this repository
spiffe/              six libraries, one server module, the vendored protos
tls/                 the certificate the sockets share, the client
                     truststore, and GET /tls/sign-in
oid4vc/              OpenID4VCI, OpenID4VP, DID Core
admin-ui/            the console at /admin
mgmt-api/            /admin-api and its generated OpenAPI document
home/                the front door: GET / and the logo on it

env/                 the appconfig files; CONFIG_FILE selects one
docs/                this site
node-ldapjs/         a git SUBMODULE

Each directory carries a CLAUDE.md with the reasoning for the modules in it. Those are the maintainer-facing documents; this site is the user-facing one.

Four things about the layout that are load-bearing

The order in common/protocol_stack.ts is the route order. A module written in TypeScript exports registerRoutes(app) and registers nothing when it is required; that file — the composition root, loaded by server.js — calls each one in turn, and requires the modules still written in JavaScript, which register their routes at that require. Express applies middleware only to routes added after it, so that one sequence decides which handler wins. There is a table of the ordering constraints in the root CLAUDE.md; each one is a real dependency rather than a preference.

common/vendored/ is not to be edited. Those files are byte-identical copies of files in the OAuth2/OIDC Debugger, and two of that project’s tests exist to keep them that way. contexts/ sits inside that directory rather than at the root precisely so that bbs2023.js — which resolves path.join(__dirname, 'contexts') — did not have to be edited when it moved.

node-ldapjs is a submodule and must sit inside this package root. npm installs a file: dependency as a symlink and node resolves that package’s own requires by walking up from where the real directory lives, so a copy one level up never reaches node_modules here. The failure is Cannot find module 'abstract-logging' from inside ldapjs.

CONFIG_FILE is made absolute before anything reads it. Fourteen modules read the appconfig file directly for a log level, and node resolves a relative require() against the directory of the module doing the requiring — which was harmless while every module was in the package root and is not now. common/config_file.js is the one place that decides it, and it is required first by server.js, common/config.js and common/helpers.js.

Adding an endpoint

Costs one entry in sts_metadata.ts. That page reads the endpoint list off the live router so it cannot go stale by omission, but it reports two kinds of drift and the parent project’s test fails on both: a route registered and undescribed, and a description whose path is not registered (what a rename produces).

Coverage notes there must start full, partial or mock and say what is missing. A list of fifty specifications that did not mention that this service checks no passwords would be the most misleading thing in the repository.

Adding a console page

A control added to /admin gets an operation on /admin-api in the same change, and both get pagination. An API that covers eight of nine controls is worse than one that covers none, because the ninth is found by a caller who has already written the code that assumed it.

That is cheap rather than a matter of discipline: every API operation calls the same action function the console’s form posts to, every API GET calls the same JSON view the page’s ?format=json answers, and the OpenAPI document is generated from the operation table — so an operation cannot exist and be undocumented, nor be documented and not exist.

What no code here can check is a new console control with no row. Nothing in this service can see a form appear on a page, so the parity is asserted from outside by this repository’s own tests/vendored/admin_api.js, which reads the facts off the service rather than off a list in the test.

Tests

There are none in this repository yet, and that is the main gap. The tests that cover this service live in the parent project. The root CLAUDE.md lists which ones should be ported and what each covers, and names the two surfaces with no test in either repository — SCIM, which would be the cheapest to write, and SPIFFE, which is the largest untested surface here.