12.5K
TypeScript LOC
18
REST endpoints
5
modeled subsystems
7272
packages/ LOC
4630
apps/ LOC

dirsim is not an LDAP/Kerberos wire-protocol reimplementation. The concepts and enforcement semantics match AD; the transport is HTTP+JSON. Every write goes through a Validate → Authorize → Append DirectoryEvent → Publish → Project pipeline, which is what makes scenario reset, time travel, and replication-delay simulation work. NT hashes are computed for real so external attack tooling can be exercised against exported data, but no real wire crypto goes over the network.

Tech scope

  • Single API process (apps/api) with internal service boundaries — the "services" in the architecture doc are folders / packages, not separate processes.
  • Pure core, impure edges. @dirsim/core is pure functions only (SID/DN parsing, LDAP filter eval, NT-hash, UAC bits, well-known SIDs); all I/O lives in apps/api.
  • Store is in-memory, persisted by snapshots when SIM_DATA_DIR is set; auto-restores on boot and snapshots every SIM_SNAPSHOT_INTERVAL_SECONDS (also on SIGTERM).
  • Production guard: NODE_ENV=production refuses to boot unless API_JWT_SECRET, API_OPERATOR_PASSWORD, and API_CORS_ORIGINS (no *) are set.

Architecture

Event-sourced mutation pipeline. Every write is validated with Zod, authorized against operator RBAC, appended to the event log, published, and projected into the in-memory directory. The event log is the source of truth; the directory is a projection. Scenarios reset by replaying from a starting event index; replication-delay simulation works by holding events back per replica.

Kerberos is simulated, not real. Tickets are signed JWTs with AD-shaped claims (PAC, SIDs, group membership, four delegation modes) — sufficient to exercise PowerView, BloodHound, and Rubeus-style tooling against the export. NT hashes are computed for real so the directory can stand up against credential-stuffing exercises.

Workspace map

  • @dirsim/schemas — Zod schemas, single source of truth.
  • @dirsim/core — pure functions: SID, DN, LDAP filter, NT-hash, UAC, well-known SIDs.
  • @dirsim/store — in-memory directory: CRUD, search, membership, events.
  • @dirsim/kerberos — KDC sim: AS/TGS/AP, PAC, S4U, Kerberoast, AS-REP roast.
  • @dirsim/policy — Resultant Set of Policy engine (LSDOU, Block Inheritance, Enforced, loopback).
  • @dirsim/sim — virtual clock, replication topology, scenarios, agent tools.
  • @dirsim/ldap-bridge — read-only LDAPv3 TCP listener.
  • @dirsim/seed-contoso — example forest fixture: contoso.local + fabrikam.local trust.
  • @dirsim/api — Fastify gateway: REST, SSE, agent tools, /metrics, /healthz.
  • @dirsim/web — Next.js 14 admin console.
forest A forest B OU 1 OU 2 OU 3 OU 4 KDC
modeled topology · as of 2026-04-26
client REST sim core event bus SSE Next.js console
as of 2026-04-26
packages · 7272 apps · 4630 seeds
apps/ + packages/ + seeds/ · as of 2026-04-26

Surface

The surface is a small REST API plus an SSE event channel and a Next.js console that consumes both. Operators describe a topology — forests, trusts, OUs, GPOs — in plain JSON; the simulator boots that into a running model with replication latency and Kerberos state machines that you can poke and observe in real time. Useful for “what does this misconfiguration actually do” experiments without standing up a real lab.

Constraints

This is a behavioral simulator, not an emulator. It does not implement the LDAP wire protocol or the SMB stack; it implements the state machine that a real AD goes through, with realistic timings and failure modes. That choice keeps the surface comprehensible and the model testable, but it means dirsim cannot stand in for a real DC for any tool that talks LDAP. The dividing line is intentional and called out at the API boundary.

:/ ESC