Complete docs/engineering/ suite — 12 documents covering company overview, system architecture, tech stack ADRs, codebase structure, service deep dives, annotated code walkthroughs, dev setup, engineering workflow, testing strategy, deployment/ops, SDK guide, and README index. All content verified against source files. All 82 tasks in openspec/changes/engineering-docs/tasks.md marked complete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
147 lines
11 KiB
Markdown
147 lines
11 KiB
Markdown
# Codebase Structure
|
|
|
|
---
|
|
|
|
## 1. Annotated Directory Tree
|
|
|
|
```
|
|
sentryagent-idp/
|
|
├── src/ # Express application source — controllers, services, middleware, repositories, routes
|
|
│ ├── app.ts # Express app factory — creates and configures the app; does NOT call listen
|
|
│ ├── server.ts # Entry point — calls listen, handles SIGTERM/SIGINT/SIGHUP
|
|
│ ├── types/ # Canonical TypeScript interfaces and type definitions
|
|
│ ├── controllers/ # HTTP layer — extract/validate inputs, call services, build responses
|
|
│ ├── services/ # Business logic — pure domain operations, no HTTP knowledge
|
|
│ ├── repositories/ # Database and Redis access — parameterized SQL, no logic
|
|
│ ├── middleware/ # Cross-cutting request concerns — auth, OPA, rate-limit, metrics, error handling
|
|
│ ├── routes/ # Express router definitions — wiring only, no logic
|
|
│ ├── utils/ # Shared pure utilities — errors, validators, crypto, JWT helpers
|
|
│ ├── vault/ # HashiCorp Vault KV v2 client
|
|
│ ├── metrics/ # Prometheus metrics registry — all Counter and Histogram definitions
|
|
│ ├── db/ # PostgreSQL pool factory and SQL migration files
|
|
│ └── cache/ # Redis client factory
|
|
├── tests/ # Jest test suite — mirrors src/ structure (unit/ and integration/)
|
|
├── dashboard/ # React 18 + Vite 5 web dashboard SPA
|
|
│ ├── src/ # Dashboard source — pages, components, auth, API client
|
|
│ └── dist/ # Built dashboard — served by Express at /dashboard (git-ignored)
|
|
├── sdk/ # Node.js SDK (@sentryagent/idp-sdk) — TypeScript, auto token refresh
|
|
├── sdk-python/ # Python SDK (sentryagent-idp) — sync + async clients
|
|
├── sdk-go/ # Go SDK (github.com/sentryagent/idp-sdk-go) — context-aware, goroutine-safe
|
|
├── sdk-java/ # Java SDK (ai.sentryagent:idp-sdk) — builder pattern, CompletableFuture
|
|
├── policies/ # OPA policy files
|
|
│ ├── authz.rego # Rego policy — normalise_path + scope-intersection allow rule
|
|
│ └── data/scopes.json # Endpoint permission map — used by Rego and TypeScript fallback
|
|
├── terraform/ # Terraform infrastructure as code
|
|
│ ├── modules/ # Reusable modules: agentidp, lb, rds, redis
|
|
│ └── environments/ # Environment configs: aws/ (ECS+RDS+ElastiCache), gcp/ (Cloud Run+SQL+Memorystore)
|
|
├── monitoring/ # Prometheus and Grafana configuration
|
|
│ ├── prometheus/ # prometheus.yml scrape configuration
|
|
│ └── grafana/ # Grafana provisioning YAML and dashboard JSON files
|
|
├── docs/ # All project documentation
|
|
│ ├── engineering/ # Internal engineering knowledge base (this directory)
|
|
│ ├── developers/ # End-user API reference and developer guides
|
|
│ ├── devops/ # Operator runbooks and environment variable reference
|
|
│ ├── agntcy/ # AGNTCY alignment documentation
|
|
│ └── openapi/ # OpenAPI 3.0 specification files
|
|
├── openspec/ # OpenSpec change management — proposals, designs, specs, tasks, archives
|
|
├── Dockerfile # Multi-stage production build (build + runtime stages)
|
|
├── docker-compose.yml # Local development: PostgreSQL 14 (port 5432) + Redis 7 (port 6379)
|
|
├── docker-compose.monitoring.yml # Monitoring overlay: Prometheus (port 9090) + Grafana (port 3001)
|
|
├── package.json # Node.js dependencies and npm scripts
|
|
├── tsconfig.json # TypeScript strict configuration — compiled to dist/
|
|
└── jest.config.ts # Jest configuration — ts-jest, test timeouts, coverage thresholds
|
|
```
|
|
|
|
---
|
|
|
|
## 2. src/ Subdirectory Roles
|
|
|
|
| Directory | Role | Rule |
|
|
|-----------|------|------|
|
|
| `src/controllers/` | Receive HTTP requests, extract and validate inputs using Joi, call service methods, serialise responses | No business logic — controllers are thin wrappers that translate HTTP into service calls |
|
|
| `src/services/` | All business logic — free-tier limit enforcement, domain rule evaluation, orchestration of repository calls and audit events | Never import from controllers or routes; never know about `req` or `res` |
|
|
| `src/repositories/` | All database and Redis queries — parameterized SQL via `node-postgres`, Redis commands via `redis` client | Only called from services; never called directly from controllers; no business logic |
|
|
| `src/middleware/` | Cross-cutting request concerns — `authMiddleware`, `opaMiddleware`, `rateLimitMiddleware`, `metricsMiddleware`, `errorHandler` | Applied at router or app level in `src/app.ts`; never import from controllers |
|
|
| `src/routes/` | Map HTTP paths and methods to middleware chains and controller methods | Wiring only — no logic, no validation, no business rules |
|
|
| `src/utils/` | Shared pure utilities — `errors.ts`, `validators.ts`, `crypto.ts`, `jwt.ts`, `asyncHandler.ts` | No side effects; no imports from services or controllers |
|
|
| `src/types/` | All TypeScript type definitions, interfaces, and enums — the single source of truth for all shared types | Imported everywhere; never imports from anywhere else in `src/` |
|
|
| `src/vault/` | `VaultClient` — wraps HashiCorp Vault KV v2 operations; constant-time secret verification | Only instantiated by `createVaultClientFromEnv()` in `src/app.ts`; passed to services via constructor injection |
|
|
| `src/metrics/` | Prometheus metrics registry — all `Counter` and `Histogram` definitions in one place | Only file that calls `new Counter()` or `new Histogram()`; all other files import from here |
|
|
| `src/db/` | PostgreSQL connection pool factory (`pool.ts`) and numbered SQL migration files in `migrations/` | Pool is a singleton created once in `src/app.ts` and passed to repositories |
|
|
| `src/cache/` | Redis client factory — creates and caches a single `redis` client instance | Client is a singleton created once in `src/app.ts` and passed to repositories |
|
|
|
|
---
|
|
|
|
## 3. Where to Add New Code
|
|
|
|
| I need to add... | Where it goes | Example |
|
|
|-----------------|---------------|---------|
|
|
| A new API endpoint | `src/routes/` (wire it), `src/controllers/` (HTTP layer), `src/services/` (business logic), `src/repositories/` (data access) | Adding `DELETE /api/v1/agents/:id/credentials/:credId/bulk` |
|
|
| A new business rule | `src/services/[relevant]Service.ts` | Enforcing a maximum of 5 credentials per agent |
|
|
| A new database table | `src/db/migrations/` — new numbered SQL file (append-only) | Adding an `agent_groups` table as `005_create_agent_groups.sql` |
|
|
| A new authorisation policy rule | `policies/authz.rego` + `policies/data/scopes.json` | Adding a new scope `reports:read` for a `GET /api/v1/reports` endpoint |
|
|
| A new shared error type | `src/utils/errors.ts` | `VaultUnavailableError` extending `SentryAgentError` |
|
|
| A new environment variable | `src/utils/config.ts` (if it exists) or the relevant consumer file + `docs/devops/environment-variables.md` | `RATE_LIMIT_MAX` controlling the rate-limit ceiling |
|
|
| A new Prometheus metric | `src/metrics/registry.ts` | A `Histogram` for Vault lookup duration |
|
|
| A new TypeScript type used in 2+ files | `src/types/index.ts` | A new `AgentGroupMembership` interface |
|
|
|
|
---
|
|
|
|
## 4. Key Files
|
|
|
|
**`src/app.ts`**
|
|
Creates and configures the Express application. Registers all middleware (helmet, cors,
|
|
morgan, body parsers, metricsMiddleware), instantiates all infrastructure singletons
|
|
(PostgreSQL pool, Redis client, VaultClient), constructs the full dependency graph
|
|
(repositories → services → controllers), and mounts all routers. Returns the configured
|
|
`Application` without calling `listen`. Tests import `createApp()` directly — this is
|
|
the design decision that makes integration tests possible without binding a port.
|
|
|
|
**`src/server.ts`**
|
|
The only file that calls `app.listen()`. Loads environment variables via `dotenv.config()`,
|
|
calls `createApp()`, binds the port from `PORT` env var (default 3000), and registers
|
|
`SIGTERM`, `SIGINT` (graceful shutdown), and `SIGHUP` (OPA policy hot-reload via
|
|
`reloadOpaPolicy()`) signal handlers. This file is never imported by tests.
|
|
|
|
**`src/types/index.ts`**
|
|
The canonical type definition file for the entire project. Contains all exported
|
|
interfaces (`IAgent`, `ICredential`, `ITokenPayload`, `IAuditEvent`, etc.), union types
|
|
(`AgentStatus`, `AgentType`, `AuditAction`, etc.), and the global Express `Request`
|
|
augmentation that adds `req.user?: ITokenPayload`. If a type is needed in two or more
|
|
files, it lives here — never redefined inline.
|
|
|
|
**`src/utils/errors.ts`**
|
|
The `SentryAgentError` base class and all typed error subclasses. Every error thrown
|
|
in the application must extend `SentryAgentError` — never `throw new Error('string')`.
|
|
The `errorHandler` middleware in `src/middleware/errorHandler.ts` maps
|
|
`SentryAgentError` subclasses to their `httpStatus` codes and serialises the response
|
|
as `IErrorResponse { code, message, details }`.
|
|
|
|
**`docker-compose.yml`**
|
|
Starts PostgreSQL 14 (Alpine) on port 5432 with database `sentryagent_idp` and
|
|
Redis 7 (Alpine) on port 6379. Used for local development only. Both services have
|
|
health checks so `depends_on` conditions work correctly. The `app` service mounts
|
|
`./src` as a read-only volume for live code reloading.
|
|
|
|
**`tsconfig.json`**
|
|
TypeScript compiler configuration. `strict: true` enables the full suite of strictness
|
|
checks. `target: ES2022`, `module: commonjs` (the project compiles to CommonJS for
|
|
Node.js compatibility). `outDir: ./dist`, `rootDir: ./src`. The `noUnusedLocals` and
|
|
`noUnusedParameters` flags are enabled — unused code is a compile error. Never disable
|
|
these flags.
|
|
|
|
---
|
|
|
|
## 5. DRY Enforcement
|
|
|
|
Every piece of logic lives in exactly one place. Violations are CTO-blocking.
|
|
|
|
| Concern | Single source of truth | Violation pattern to reject |
|
|
|---------|----------------------|----------------------------|
|
|
| Business logic | One service method — called from multiple controllers if needed | Business logic duplicated in a route handler or controller |
|
|
| Database queries | One repository method — never repeated inline | SQL written directly in a service or controller |
|
|
| Error types | `src/utils/errors.ts` — imported wherever errors are thrown | `new Error('AGENT_NOT_FOUND')` instead of `new AgentNotFoundError()` |
|
|
| TypeScript types | `src/types/index.ts` — imported in every consumer file | An interface defined inline in a service file |
|
|
| Validation logic | `src/utils/validators.ts` — Joi schemas used in controllers | Validation logic duplicated across multiple controllers |
|
|
| Prometheus metrics | `src/metrics/registry.ts` — one definition per metric | A second `new Counter({ name: 'agentidp_tokens_issued_total' })` anywhere |
|