Files
sentryagent-idp/docs/engineering/04-codebase-structure.md
SentryAgent.ai Developer 8cabc0191c docs: commit all Phase 6 documentation updates and OpenSpec archives
- devops docs: 8 files updated for Phase 6 state; field-trial.md added (946-line runbook)
- developer docs: api-reference (50+ endpoints), quick-start, 5 existing guides updated, 5 new guides added
- engineering docs: all 12 files updated (services, architecture, SDK guide, testing, overview)
- OpenSpec archives: phase-7-devops-field-trial, developer-docs-phase6-update, engineering-docs-phase6-update
- VALIDATOR.md + scripts/start-validator.sh: V&V Architect tooling added
- .gitignore: exclude session artifacts, build artifacts, and agent workspaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 02:24:24 +00:00

164 lines
13 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
├── sdk-rust/ # Rust SDK (sentryagent-idp crate) — async, tokio, reqwest, typed errors
├── 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
├── portal/ # Developer Portal — Next.js 14 App Router, Tailwind CSS
│ ├── app/ # Next.js App Router pages (get-started, pricing, sdks, analytics, settings, login)
│ ├── components/ # Shared UI components (Nav.tsx, SwaggerExplorer.tsx, GetStartedWizard.tsx)
│ ├── hooks/ # React hooks (useAuth.ts)
│ └── types/ # TypeScript type definitions for portal-only types
├── 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
├── tests/ # Jest test suite — mirrors src/ structure
│ ├── unit/ # Unit tests (mocked dependencies) — mirrors src/
│ ├── integration/ # Integration tests (real DB + Redis)
│ ├── agntcy-conformance/ # AGNTCY conformance test suite (separate Jest config)
│ └── load/ # k6 load test scripts
├── 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 |
| `src/config/` | Configuration constants — `tiers.ts` exports `TIER_CONFIG`, `TIER_RANK`, `TierName`, and `isTierName()` type guard | Imported by `TierService` and `tierMiddleware`; never imports from services |
| `src/middleware/tier.ts` | Tier enforcement middleware — reads org tier from `TierService`, checks daily call counter in Redis, throws `TierLimitError` (429) when limit is exceeded, increments counter on pass | Applied only to API routes; skips `/health`, `/metrics`, and static file routes |
---
## 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 |
| A new tier-gated feature | `src/config/tiers.ts` (add limit field) + `src/middleware/tier.ts` (add check) + service (enforce) | Adding a `maxWebhooksPerOrg` tier limit |
| A webhook event handler | `src/services/WebhookService.ts` (add event type to `WebhookEventType`) + the producer that calls `void webhookService.dispatch(orgId, eventType, payload)` | Emitting `agent.decommissioned` events to subscriber URLs |
| A new analytics metric type | `src/services/AnalyticsService.ts` (call `recordEvent(tenantId, 'new_metric')` in the relevant service using `void`) | Recording `credential_rotated` events for analytics |
| A new DID endpoint | `src/controllers/DIDController.ts` + `src/routes/did.ts` + `src/services/DIDService.ts` (if new method needed) + `policies/data/scopes.json` | Adding `GET /api/v1/agents/:id/did/rotate-key` |
---
## 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 |