Request Lifecycle¶
Every authenticated HTTP request lands in DRF the same way. Knowing this pipeline cold is what makes debugging tractable.
Pipeline¶
Client Server
────── ──────
HTTP request ──────────────────────────────────► ┌──────────────────────┐
(Org, Authorization, body) │ Middleware stack │
│ CORS │
│ CSP │
│ Tenant (Org:) │ ← swaps DB
│ Encryption │
│ JWT/OAuth2 │ ← request.user
│ Elastic APM │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ URL router │
│ NimbusVault/urls.py │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ DRF view │
│ (method per verb) │
└──────────┬───────────┘
│ data=request.data
┌──────────▼───────────┐
│ Serializer │
│ .is_valid() │ ── 400 on fail
└──────────┬───────────┘
│ validated_data
┌──────────▼───────────┐
│ Orchestrator │
│ Registry.get(...) │
│ .stage_*() │
└──────────┬───────────┘
│ composes steps
┌──────────▼───────────┐
│ bll/<Domain>/ │
│ HelperFunctions │ ← pure
│ GetApisbll / │
│ PostApisbll │ ← business
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ service.py │ ← ONLY place
│ CRUD primitives │ that calls
└──────────┬───────────┘ the ORM
│ Model.objects.*
┌──────────▼───────────┐
│ Postgres / ES / │
│ Redis / Neo4j / ... │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ mainorchestrator │
│ .execute() │ ← runs steps,
│ │ rolls back
│ │ on failure
└──────────┬───────────┘
│
Response (data, status) ◄────────────────────────────────────┘
Middleware stack¶
Defined in NimbusVault/CustomMiddlewares.py and NimbusVault/settings.py. Order matters.
| Middleware | Purpose |
|---|---|
| CORS | Allow listed origins (configured in VaultSettings.json). |
| CSP | Content Security Policy headers. |
| Tenant | Reads Org: HTTP header → resolves to a tenant DB → swaps the DB connection used by the ORM for this request. Elasticsearch index names are also namespaced by the org slug. |
| Encryption | Decrypts encrypted-payload fields when present. |
| JWT / OAuth2 | Validates the Authorization: JWT <token> or Authorization: Bearer <token> header; populates request.user. Keycloak / SAML2 / LDAP are alternatives — picked by VaultSettings.json. |
| Elastic APM | WSGI + ASGI tracing middlewares. Spans show up in APM with the URL pattern as transaction name. |
Header convention
The auth scheme prefix in this codebase is JWT, not Bearer. Frontend code sends authorization: JWT eyJ.... Tenant routing requires Org: <slug> as an HTTP header (NOT just ?org= in the query string).
URL routing¶
NimbusVault/urls.py is the single root urlconf. Three groups of patterns:
- Built-in core URLs: admin, Prometheus metrics, SAML2, Swagger schema/UI.
- VaultManagement domain URLs: every file in
VaultManagement/urls/isinclude()'d under the/vault/prefix (ModelName.url_vault). -
Plugin URLs: every plugin listed in
ACTIVE_PLUGINShas itsPlugins/<Name>/urls.pyincluded automatically:
So adding endpoints requires only:
- A new path inside an existing
VaultManagement/urls/<Domain>.py, OR - A new domain module + an
include(...)line inNimbusVault/urls.py, OR - A new plugin under
Plugins/<Name>/plus listing it inVaultSettings.jsonBase.active_plugins.
The full mechanic is spelled out in API Creation Guide → The contract.
Multi-tenancy¶
Tenant isolation is database-level, not row-level. TenantMiddleware reads Org: and switches the active DB connection for the request's duration. Every ORM call inside the request lands in that tenant's database. No application code needs to filter by tenant — there is no shared tenant table.
Elasticsearch indices are also tenant-scoped ({tenant}_{logical_index_name}).
This has consequences:
- Background tasks (Celery) must explicitly pass the tenant. Look at how
Orchestrators/WorkerTasks/handlesorgkwargs. - Cross-tenant operations are a code smell; if you need one, justify it in the PR description.
Errors¶
Everything raises typed errors from VaultErrors/BackendErrors.py:
from VaultErrors.BackendErrors import errors
raise errors.bad_request.ValidationError("...")
raise errors.bad_request.NotFound("...")
raise errors.bad_request.PermissionDenied("...")
DRF exception handlers turn these into JSON responses with the right HTTP status. Do not raise plain Exception or DRF's built-in exceptions inside the BLL — always go through errors.*.
Auth on views¶
Views inherit from NimbusVaultConstants.CommonConstants.CommonConstants.ModelAPIView, which applies default authentication + permission classes. Anonymous endpoints must explicitly override authentication_classes = [] and permission_classes = [].