--- since: 1.2.1 --- # DjangoPlay — `core` App ## 1. What `core` Is `core` is DjangoPlay's **cross-cutting infrastructure app**. It owns no business models and no user-facing features. Its job is to give every other app four things "for free": 1. **Abstract model base classes** (`models/`) — soft delete, timestamps, audit-actor FKs. 2. **Request-scoped context** (`execution_context/`, `request_context.py`) — who is acting, from where, in what timezone, on which request — available anywhere in the call stack without passing the request object around. 3. **A domain event bus** (`events/`) — a decoupled "what happened" → "who cares" mechanism so business services don't need to know about audit logging, telemetry, or notifications. 4. **HTTP-layer plumbing** (`middleware/`, `admin_filters/`) — request IDs, client IP capture, timezone activation, CSRF failure UX, API traffic logging, and a generic admin autocomplete/filter API. `core` is deliberately **not** the orchestration layer for any of this — per `docs/adr/enterprise-audit-trail-domain-event.md`, the explicit design principle is *"Business services express what happened; infrastructure decides how to observe it."* `core` provides the "what happened" plumbing (events, context); the `audit` app is what actually subscribes to it and persists history. This split matters when you're tracing behavior: **`core` alone does almost nothing observable — it needs `audit.apps.AuditConfig.ready()` to wire its subscribers at startup** (see §5.4). --- ## 2. Module Map ``` core/ ├── models/ Abstract base model classes (AuditFieldsModel, TimeStampedModel, ActiveManager) ├── execution_context/ ContextVar-based "who/where/when" runtime actor (current, in active use) ├── request_context.py A second, near-duplicate ContextVar module (client_ip/request_id/timezone) — see §7.1 ├── events/ Domain event bus: contracts, emitter, registry, factories, severity/persistence policy │ └── runtime/ Dispatch timing/metrics + an admin-only metrics endpoint │ └── subscribers/ Built-in logging subscriber ├── telemetry/ In-process (non-persisted) event counters + a snapshot endpoint ├── middleware/ 7 middleware classes (request id, client IP, timezone, CSRF override, API logging, etc.) ├── admin_filters/ Generic "distinct values for a field" API used by admin-side autocomplete/select2 filters ├── utils/ Small helpers: lazy Redis client proxy, timezone validation, CLI spinner ├── urls.py Defines a telemetry route — but see §7.2, it is dead code, never included anywhere └── tests/ Middleware, model, and utils unit tests ``` --- ## 3. Abstract Models (`core/models/`) Two abstract base classes, exported from `core/models/__init__.py`: ### 3.1 `AuditFieldsModel` (`models/AuditFieldsModel.py`) Adds three nullable FK fields to `AUTH_USER_MODEL` (`users.UserIdentity`): `created_by`, `updated_by`, `deleted_by`. All `on_delete=models.SET_NULL`. `related_name` uses `%(app_label)s_%(class)s_created_by` etc. so it's safe to inherit from multiple apps without clashing reverse accessors. Purely a field mixin — no methods, no signals. ### 3.2 `TimeStampedModel` + `ActiveManager` (`models/LifecycleModel.py`) The soft-delete/lifecycle base used across the domain apps (`teamcentral`, `entities`, `invoices`, etc. — anything with an `ActiveManager`/`is_active` pattern likely inherits this). Fields: `created_at` (auto_now_add), `updated_at` (auto_now), `deleted_at` (nullable), `is_active` (default `True`). Methods: - **`soft_delete(*, user=None, reason=None, **kwargs)`** — no-ops if already deleted; otherwise sets `deleted_at=now()`, `is_active=False`, conditionally updates `deleted_by` (only `if hasattr(self, "deleted_by")`, i.e. only if the model also inherits `AuditFieldsModel`), saves with `update_fields`, then **sends the Django signal `audit.signals.events.post_soft_delete`** with `sender=self.__class__, instance=self, user=user, reason=reason`. - **`restore(*, user=None, **kwargs)`** — inverse of the above; sends `audit.signals.events.post_restore`. **Important cross-app coupling to flag**: `core.models.LifecycleModel` imports directly from `audit.signals.events`. This means `core` is not actually audit-agnostic at the model layer — every model using `TimeStampedModel` has a hard dependency on the `audit` app being installed and its signal names not changing. This is a deliberate, narrow coupling (only two signal sends), but it's worth knowing if you ever try to make `core` a standalone/reusable package. `ActiveManager` — a `models.Manager` subclass that filters `deleted_at__isnull=True, is_active=True`. Models must attach this explicitly (e.g. `objects = ActiveManager()`); it isn't auto-attached by `TimeStampedModel`. --- ## 4. Execution Context (`core/execution_context/`) This is the "current runtime actor" mechanism — the officially current implementation per the audit/domain-event ADR (§4.1: *"Execution context is now infrastructure-owned. Audit no longer owns runtime context."*). **`context.py`** — five module-level `contextvars.ContextVar`s: `actor`, `request_id`, `client_ip`, `timezone`, `user_agent`. Async-safe by construction (contextvars are the correct primitive for this under ASGI/async views). **`actor.py`** — `ExecutionActor`, a frozen dataclass: `id: int | None`, `type: str`, `label: str | None = None`. **`system_actor.py`** — `SystemActor`, a plain class (not a dataclass) with class-level attributes `id=1`, `type="System"`, `label="System"`, `is_system=True`. Used to represent management-command/background-job actors that aren't a real `UserIdentity`. **`helpers.py`** — setters (`set_actor`, `set_request_id`, `set_client_ip`, `set_timezone`, `set_user_agent`) and getters (`get_actor(default=None)`, etc., all `value.get() or default`). Also exposes **`set_management_command_context(*, command_name)`** — builds a `SystemActor`, sets its `label` to the command name, sets it as the current actor, and hardcodes `client_ip = "127.0.0.1"` with the comment *"This is for bulk import from local terminal only always."* This is the hook `devtools` management commands are expected to call so that audit trail entries generated by bulk imports are attributable to a named command rather than `None`. **`middleware.py`** — `ExecutionContextMiddleware`. Populates all five context vars per-request from whatever earlier middleware already attached to `request` (`request.request_id`, `request.client_ip`, `request.timezone` — all set by *other* middlewares earlier in the chain, see §6). Builds `ExecutionActor(id=user.pk, type="user", label=user.email)` if authenticated. **Wrapped in a broad `try/except Exception: logger.exception(...)`** — by design this middleware must never break a request even if context enrichment fails. Runs the response through unconditionally either way. --- ## 5. Domain Event System (`core/events/`) This is the most architecturally significant part of `core`. It implements a synchronous, in-process publish/subscribe bus for **domain events** — decoupling "a thing happened in a business service" from "something needs to react to that" (audit persistence, structured logging, telemetry, future notifications). ### 5.1 The Event Contract (`contracts.py`) ```python @dataclass(slots=True) class DomainEvent: name: str category: str = EVENT_CATEGORY_SYSTEM # "system" default severity: str = EVENT_SEVERITY_INFO # "info" default target: object | None = None # usually a model instance metadata: dict = field(default_factory=dict) occurred_at: datetime = field(default_factory=timezone.now) ``` Categories (`constants.py`): `security`, `financial`, `data_governance`, `administrative`, `system`. Severities: `info`, `warning`, `critical`. ### 5.2 Emitting an Event — the public API business services use `core.events.__init__` re-exports exactly two symbols for consumers: `DomainEvent` and `emit_event`. **`emit_event()`** (`helpers.py`) is the canonical entrypoint: ```python emit_event(name="invoice.paid", target=invoice, metadata={...}, category=..., severity=...) ``` - If `severity` isn't passed, it's resolved automatically via `resolve_event_severity(name)` (`severity.py`), which takes the **last dot-segment of the event name** (e.g. `"invoice.paid"` → `"paid"`... actually note: it splits on `.` and takes the *last* token, so `"industry.deleted"` → `"deleted"` → maps to `warning` via `EVENT_SEVERITY_MAP`). Unmapped actions default to `info`. - `severity.py`'s lookup table: `created`/`updated`/`restored` → `info`; `deleted`/`failed`/`error`/`login_failed` → `warning`; `permission_changed`/`payment_failed` → `critical`; `refund_issued` → `warning`. - The event is built via `build_event()` (`builders.py`, a thin dataclass constructor) and handed to `emit()`. ### 5.3 Dispatch (`emitter.py`, `registry.py`, `runtime/`) `emit(event)`: 1. Validates the event name via `audit.governance.validation.validate_event_name` — **note this is a reverse dependency: `core.events.emitter` imports from `audit`**, mirroring the `LifecycleModel` → `audit.signals` coupling in §3. `core` and `audit` are mutually intertwined despite the ADR's stated goal of `core` owning context/events independently of audit. 2. If invalid, logs a warning and returns (**never raises**). 3. Iterates every registered subscriber (`registry.get_subscribers()`) and calls `runtime.dispatching.safely_dispatch_event(subscriber=..., event=...)` for each. `registry.py` — `SUBSCRIBERS` is a plain module-level `dict` keyed by `f"{handler.__module__}.{handler.__name__}"`. `subscribe(handler)` blocks duplicate registration (logs a warning, no-ops) rather than raising, which is why every call site in `audit/apps.py` defensively checks `if handler not in get_subscribers(): subscribe(handler)` before calling `subscribe` — that check is actually redundant given `subscribe()` already dedupes internally, but it's harmless. `runtime/dispatching.py::safely_dispatch_event` — wraps each subscriber call in `event_timer()` (a tiny context manager using `perf_counter`, `runtime/timing.py`), records the duration via `runtime/metrics.py::record_dispatch_timing` (an in-memory dict, `EVENT_DISPATCH_TIMES[subscriber_name] = duration` — **last-write, not cumulative/averaged**), and if the duration exceeds `SLOW_EVENT_THRESHOLD_SECONDS = 0.5`, logs a warning via `runtime/safety.py::log_slow_subscriber`. **Any exception from within a subscriber is caught and logged (`logger.exception`) — dispatch to one subscriber failing never prevents dispatch to the next, and never propagates back to the emitting business code.** This "never raises" guarantee is repeated in code comments at three separate layers (`emit()`, `emit_event()`, `safely_dispatch_event()`) — it's clearly a deliberate, hardened design invariant, not an accident. - **Dead-code note**: `runtime/exceptions.py` contains an exact duplicate of `is_slow_subscriber`/`log_slow_subscriber` that also live in `runtime/safety.py`. `dispatching.py` imports from `safety.py`; nothing imports from `exceptions.py`'s versions of those two functions. Harmless but should be cleaned up — likely a copy-paste leftover from a refactor. ### 5.4 Who Actually Subscribes (this happens **outside** `core`) `core` ships zero subscriber *registrations* itself — it only ships the subscriber registry and one candidate subscriber (`events/subscribers/logging.py::logging_subscriber`, structured `logger.info` with `event.name/category/severity/metadata`) plus `core/telemetry/subscribers.py::telemetry_subscriber`. **Neither is wired up unless `audit` is installed and boots.** The actual wiring happens in `audit/apps.py::AuditConfig.ready()`, which imports and registers, in order: 1. `audit.subscribers.audit_persistence_subscriber` (writes `AuditEvent` rows — see the future `audit` app doc) 2. `core.events.subscribers.logging.logging_subscriber` 3. `core.telemetry.subscribers.telemetry_subscriber` A module-level `_SUBSCRIBERS_REGISTERED` boolean guard in `AuditConfig` prevents double-registration if `ready()` fires more than once (e.g. under certain test runners or autoreload). **Practical implication: if you ever disable the `audit` app, or if `audit` fails to boot, `core`'s event bus goes completely silent — no logging, no telemetry, nothing — with no error raised anywhere**, because every layer is designed to swallow failures silently. ### 5.5 Deciding What Gets Persisted (`policies.py`) `should_persist_event(event)` is the policy function `audit`'s persistence subscriber is expected to consult (referenced by `audit`, not called anywhere inside `core` itself). Logic: - Exact-match denylist `NON_PERSISTED_EXACT_EVENTS`: `email.queued`, `email.sent`, `email.delivered`, `cache.refreshed`, `task.started`, `task.completed`. - Substring denylist `NON_PERSISTED_CONTAINS`: `.email_`, `.task_`, `.cache_`. - Otherwise, an event is only persisted if its name starts with a prefix in `get_persisted_event_prefixes()` — which is **dynamically derived** from `audit.lifecycle.registry.AUDIT_TRACKED_MODELS` (each `"app_label.ModelName"` entry becomes `f"{modelname.lower()}."`), plus two hardcoded manual prefixes `auth.` and `admin.`. This means **adding a model to `AUDIT_TRACKED_MODELS` automatically makes matching domain events persistable** without touching `core` — a nice example of the "explicit domain ownership" principle from the platform README, applied to the event layer. ### 5.6 Event Factories (`factories.py`) Convenience constructors returning plain dicts (not `DomainEvent` instances — callers still need to pass these through `build_event`/`emit_event`) for common patterns: `auth_login_event`, `auth_failed_login_event`, `invoice_paid_event`, `entity_created_event`, `entity_updated_event`, `entity_deleted_event`, `export_event`. The entity-lifecycle factories are generic — they derive the event name from `entity._meta.model_name` (e.g. any model can be passed to `entity_created_event`, not just the `entities` app's `Entity` model — the naming is a bit misleading). ### 5.7 Target Labeling & Serialization (`actors.py`, `serializers.py`) Two independent "make this object presentable/loggable" concerns: - **`actors.py::serialize_actor`** — turns an actor (Django user, `ExecutionActor`, `SystemActor`, or `None`) into `{"id", "type", "label"}`, using duck-typing (`hasattr(actor, "pk")` → real Django model) rather than isinstance checks. - **`serializers.py`** — a much larger "label resolution" system for arbitrary **targets** (not actors) attached to events, used when persisting/displaying them. `LABEL_FIELD_PRIORITY` is a generic fallback field-name search order (`full_name`, `title`, `name`, `invoice_number`, `email`, `slug`, etc.). `MODEL_LABEL_PRIORITY` is a **per-model override dict** keyed by `"app_label.ModelName"` string (e.g. `"helpdesk.BugReport": ("summary", "bug_number")`), letting specific domain models specify exactly which fields make a good human-readable label, in priority order. `resolve_target_label(target)` checks, in order: an explicit `target.audit_label()` method if present → model-specific priority fields → generic priority fields → `str(target)` → class name as last resort. `serialize_target(target)` wraps this into `{"type", "id", "label"}` for ORM objects and non-ORM objects alike. **This is a config-driven extension point**: to make a new model's audit/event history read nicely, add an entry to `MODEL_LABEL_PRIORITY` — no other `core` change needed. --- ## 6. Telemetry (`core/telemetry/`) A **deliberately lightweight, non-persisted, in-process** metrics layer — explicitly documented as such in `registry.py`'s docstring ("No persistence. No external systems."). This means **counters reset to zero on every process restart/deploy and are not shared across multiple worker processes** (each Gunicorn worker has its own counts) — fine for local dev visibility, not suitable as a real production metrics source without further work. - `registry.py` — three `collections.defaultdict(int)` globals: `EVENT_COUNTERS`, `EVENT_CATEGORY_COUNTERS`, `EVENT_SEVERITY_COUNTERS`. - `counters.py` — `increment_event_counter/category_counter/severity_counter`. - `subscribers.py::telemetry_subscriber(event)` — the function registered by `audit/apps.py` (§5.4); on every dispatched event it increments all three counters and calls `anomalies.py::evaluate_event_anomaly(event)`. - `anomalies.py` — currently **logging-only**: if `event.name` is in `HIGH_RISK_EVENTS = {"auth.login_failed", "auth.permission_changed", "auth.impersonation_started"}`, logs a `logger.warning`. Explicitly documented as a stub: *"Future-ready foundation for: alerting, notifications, SIEM integration."* - `aggregation.py::get_event_metrics()` — returns the three counter dicts as plain dicts. - `snapshots.py::build_telemetry_snapshot()` — `{"generated_at": utcnow().isoformat(), "metrics": get_event_metrics()}`. - `views.py::TelemetrySnapshotAPIView` — a DRF `APIView` with **`authentication_classes = []` and `permission_classes = []`** (i.e., explicitly, intentionally public, no auth at all) that returns `build_telemetry_snapshot()` on `GET`. --- ## 7. Findings Worth Flagging to the Team These are concrete issues surfaced by reading the actual code, not stylistic nitpicks: ### 7.1 Duplicate context-var modules `core/request_context.py` and `core/execution_context/context.py` + `helpers.py` implement **two separate, overlapping** sets of `ContextVar`s for `client_ip`, `request_id`, and `timezone` (execution_context additionally has `actor` and `user_agent`; request_context does not). Different middleware writes to different ones: - `core.middleware.client_ip.ClientIPMiddleware` and `core.middleware.timezone.TimezoneMiddleware` write into **`request_context.py`**'s vars (`set_client_ip`, `set_timezone` from `core.request_context`). - `core.execution_context.middleware.ExecutionContextMiddleware` (which runs *later* in `MIDDLEWARE`, see §8) writes into **`execution_context/context.py`**'s vars, but it reads its input values from `request.client_ip` / `request.timezone` attributes (set on the request object by the earlier middlewares) — not from `request_context.py`'s ContextVars directly. So the two modules aren't actually in conflict at runtime (the request-object attributes bridge them), but they are **redundant parallel implementations** of the same concept, and any code reaching for "the current client IP" needs to know which of the two importable APIs (`core.request_context.get_client_ip` vs. `core.execution_context.get_client_ip`) is the one actually populated in the context it's running in. This looks like a mid-migration leftover — `execution_context` reads as the newer, ADR-sanctioned module (§4); `request_context.py` reads as the module it's superseding. Worth consolidating. ### 7.2 `core/urls.py` is dead code, and is malformed ```python from django.urls import path from core.telemetry.views import TelemetrySnapshotAPIView path( "telemetry/", TelemetrySnapshotAPIView.as_view(), ) ``` This calls `path(...)` and discards the result — it never assigns to a module-level `urlpatterns` list. Nothing in `paystream/urlconf/*.py` does `include("core.urls")` (confirmed by repo-wide search — the only two `core`-namespaced URL includes actually wired up are `core.events.runtime.urls` at `/ops/events/` and `core.admin_filters.urls` at `/ops/admin-filters/`, both in `paystream/urlconf/base.py`). If anyone *did* try to `include("core.urls")`, Django would raise `ImproperlyConfigured` (no `urlpatterns` attribute). **Net effect: this file is entirely inert — the telemetry endpoint it appears to define is not reachable anywhere in the running application.** The real, working telemetry-adjacent endpoint is `EventRuntimeMetricsAPIView` at `/ops/events/metrics/` (admin-only, §5.3/§8), which is a *different* view (dispatch timing metrics) from `TelemetrySnapshotAPIView` (event/category/severity counters, §6) — so this isn't just a duplicate route, it's a genuinely orphaned feature. ### 7.3 `CSRFOverrideMiddleware` is inert; the real logic lives in a plain function `core/middleware/csrf_override.py` defines a middleware class whose docstring says outright *"Current behavior: does NOT hijack generic 403 responses; simply passes responses through"* — it's a no-op pass-through, explicitly kept only for backward compatibility. It is **not present in `MIDDLEWARE`** (confirmed against `paystream/app_settings/middleware.py`, §8). The actual CSRF-failure UX (the nicely styled error page / JSON response with "session expired" detection) is implemented by the **module-level function** `custom_csrf_failure` in the same file, wired via Django's dedicated `CSRF_FAILURE_VIEW = "core.middleware.csrf_override.custom_csrf_failure"` setting in `paystream/settings/base.py`. That setting mechanism is unrelated to the `MIDDLEWARE` list, so this isn't a bug — but the file mixes one dead class with one actively-used function in a way that's easy to misread as "this middleware handles CSRF" when it doesn't. - Minor related note: `users/views/ui/errors.py` defines its own `custom_csrf_failure(request, reason="")` that just calls `core.middleware.csrf_override.render_csrf_error(request)` (dropping the `reason`/`expired`-detection logic entirely). It's unclear from `core` alone whether this second copy is live anywhere else in `users`'s URL config — flagged here so it can be checked when documenting `users`. ### 7.4 `URLResolutionLoggingMiddleware` self-guards, but is unused anyway `core/middleware/url_resolution_debug.py` ends with a **module-level** guard: ```python if not settings.DEBUG: raise RuntimeError("URLResolutionLoggingMiddleware must not be enabled outside DEBUG") ``` This means simply *importing* this module in a non-DEBUG environment crashes the process — a strong, intentional safety rail against accidentally shipping a debug-only middleware to production. In the current branch it isn't referenced in `MIDDLEWARE` at all (§8), so the guard is currently inert, but it's a good pattern to point new engineers to if they ever add a genuinely debug-only middleware. ### 7.5 Mutable cross-app coupling from `core` into `audit` Both `core/models/LifecycleModel.py` (imports `audit.signals.events`) and `core/events/emitter.py` (imports `audit.governance.validation`) import from `audit`. Given the stated architectural goal (per the ADR) of `core` owning context/events as generic infrastructure and `audit` merely subscribing to it, these two imports are a partial violation of that layering — `core` cannot currently be imported/used (for soft-delete or for emitting *any* event) without `audit` being present and importable. Not necessarily wrong, but worth being explicit about if `core` is ever pulled out as a genuinely standalone package, or if `audit` is ever made optional. --- ## 8. Middleware — Exact Order & Role Confirmed against `paystream/app_settings/middleware.py` (the authoritative `MIDDLEWARE` list). This is the literal order Django applies them, and it explains several dependencies above (e.g. why `ExecutionContextMiddleware` can read `request.client_ip`/`request.timezone` — the middlewares that set those attributes run earlier): | # | Middleware | Owner | Purpose | |---|---|---|---| | 1 | `django_hosts.middleware.HostsRequestMiddleware` | 3rd-party | Subdomain routing (issues/docs) | | 2 | `core.middleware.request_id.RequestIDMiddleware` | **core** | Assigns/propagates `X-Request-ID`; sets `request.request_id` | | 3 | `core.middleware.client_ip.ClientIPMiddleware` | **core** | Resolves client IP (X-Forwarded-For aware); sets `request.client_ip` | | 4 | `django.middleware.security.SecurityMiddleware` | Django | Standard security headers | | 5 | `whitenoise.middleware.WhiteNoiseMiddleware` | 3rd-party | Static file serving | | 6 | `django.contrib.sessions.middleware.SessionMiddleware` | Django | Session handling | | 7 | `django.middleware.common.CommonMiddleware` | Django | — | | 8 | `django.middleware.csrf.CsrfViewMiddleware` | Django | CSRF enforcement (failure routed to `CSRF_FAILURE_VIEW`, §7.3) | | 9 | `django.contrib.auth.middleware.AuthenticationMiddleware` | Django | Attaches `request.user` | | 10 | `django.contrib.messages.middleware.MessageMiddleware` | Django | — | | 11 | `allauth.account.middleware.AccountMiddleware` | 3rd-party | django-allauth | | 12 | `core.middleware.timezone.TimezoneMiddleware` | **core** | Activates per-user tz (`user.effective_timezone` or `DEFAULT_USER_TIMEZONE = "Asia/Kolkata"`); sets `request.timezone` | | 13 | `core.execution_context.middleware.ExecutionContextMiddleware` | **core** | Populates `ExecutionActor` + all execution-context ContextVars from `request.*` attrs set above (§4) | | 14 | `django.middleware.clickjacking.XFrameOptionsMiddleware` | Django | — | | 15 | `simple_history.middleware.HistoryRequestMiddleware` | 3rd-party | Attaches history-tracking actor for `django-simple-history` | | 16 | `core.middleware.template_syntax.TemplateSyntaxErrorLoggingMiddleware` | **core** | `logger.critical`s on `TemplateSyntaxError`, then re-raises unchanged | | 17 | `core.middleware.api_request_logging.APIRequestLoggingMiddleware` | **core** | Writes `apidocs.models.APIRequestLog` rows for analytics (§9) | | 18 | `django_hosts.middleware.HostsResponseMiddleware` | 3rd-party | Subdomain routing (response side) | Not present in this list (confirmed dead/orphaned, see §7): `CSRFOverrideMiddleware`, `URLResolutionLoggingMiddleware`. --- ## 9. `APIRequestLoggingMiddleware` — Traffic Analytics Feed This is the busiest, most detailed piece of `core` middleware and worth its own section since it feeds the analytics/CSV export system referenced in project history (`apidocs.services.subdomain_stats`). **What it tracks:** - **Root domain**: only paths starting with `/api/`, `/console/`, or `/admin/`. - **Subdomains** (`issues`, `docs`): everything *except* `/static/`, `/media/`, `/dist/`, `/favicon.ico`, `/.well-known/`, `/wireless/`. - Only `GET/POST/PUT/PATCH/DELETE`. - A frozen set of always-excluded exact paths regardless of domain: session-check/relogin/me endpoints, bare `/`, `/signin/`, `/signout/`, `/wireless/`, plus (root-only) `/api/v1/users/stats/`. **Path normalization** (`_normalize_path`) — the analytics-friendly canonicalization layer: strips numeric IDs and UUIDs from paths (`/api/v1/crud/issues/252/` → `/api/v1/crud/issues/{issue_number}/`), with **context-sensitive replacement tokens** based on the preceding path segment (`attachments`→`{number}`, `comments`→`{number}`, `labels`→`{number}`, `issues`→`{issue_number}`, else→`{id}`). For the `issues` subdomain specifically, it goes further and **disambiguates same-URL POST actions** by inspecting `request.POST["action"]` — `change_status` → `/issues/{issue_number}/status/`, `add_comment` (+ `request.FILES["files"]` present) → `/issues/{issue_number}/attachment/`, `add_comment` (no files) → `/issues/{issue_number}/comment/`. This is exactly the "semantic path normalization" logic referenced in prior work on `apidocs`/`subdomain_stats` (per project history) — confirmed here as living in `core`, not `apidocs`. **Public/private API classification** (`_resolve_is_public` / `_resolve_is_public_drf`) — attempts to answer "was this endpoint callable without authentication?" for analytics purposes by actually **resolving the URL against the correct subdomain's urlconf** and inspecting the matched view's permissions. For DRF viewsets using `get_permissions()` per-action (explicitly called out: `IssueCRUDViewSet`) rather than a static `permission_classes`, it **instantiates the view class** and calls `get_permissions()` with a synthetic `action = "list"` to approximate the real check — a pragmatic but inherently approximate heuristic (a viewset could easily have `list` be public while `retrieve`/`create` are not, and this code would misclassify accordingly). Explicitly wrapped in try/except → defaults to non-public on any resolution failure. **Failure isolation**: `process_response` wraps its entire body in `try/except Exception: logger.error(..., exc_info=exc)` and **always returns `response` unchanged** — logging failures (including any DB error writing `APIRequestLog`) can never break the actual HTTP response. This mirrors the "never raise" invariant seen throughout `core.events`. **Best-effort re-authentication**: if `request.user` isn't already authenticated (e.g. session auth didn't apply), it makes a *second* attempt via `JWTAuthentication().authenticate(request)` purely so JWT-authenticated API traffic still gets attributed to a user in the log — session-based and JWT-based traffic are both captured by the same middleware. --- ## 10. `admin_filters/` — Generic Admin Autocomplete API A small, deliberately generic API (`/ops/admin-filters/options/`, admin-only via `IsAdminUser`) that powers dynamic filter/autocomplete dropdowns in the Django admin/console. It is **config-driven and allowlisted** rather than open — `ADMIN_FILTER_REGISTRY` in `registry.py` is the single source of truth for which `"app_label.ModelName"` + field combinations are queryable this way; currently only one entry is registered: `audit.AuditEvent` with fields `{action, actor_type, target_type, client_ip, category, severity}`. `services.py::get_filter_options()` enforces the allowlist (returns `[]` for unregistered model/field pairs — **fails closed**, consistent with the platform's stated "fail-closed security" philosophy), excludes null/empty values, supports an `icontains` search (`q` param), and caps results at `RESULT_LIMIT = 50`. Queries via `model.all_objects.all()` (a manager name implying it *includes* soft-deleted records — consistent with wanting historical filter values to still show up even for deleted audit targets). To extend this to a new model/field, the only `core` change needed is adding an entry to `ADMIN_FILTER_REGISTRY` — another example of the config-driven extension points seen in `events/serializers.py` (§5.7). --- ## 11. `utils/` — Small Infrastructure Helpers - **`redis_client.py`** — `_RedisClientProxy`, a lazy, thread-safe (double-checked locking via `threading.RLock`) wrapper around `django_redis.get_redis_connection()`. Exists specifically so importing `core.utils.redis_client` doesn't itself trigger a live Redis connection at Django startup/import time — connection is deferred to first actual use. Exposes `safe_get`/`safe_setex`/`safe_delete` wrapper functions that swallow all exceptions and log, returning `None`/`False`/`0` on failure rather than propagating — the same "infrastructure never breaks the request" philosophy applied to caching. - **`timezone.py`** — `normalize_timezone(tz)`: validates against `zoneinfo.available_timezones()`, falls back to `settings.DEFAULT_USER_TIMEZONE`, **raises `ValueError` for an unrecognized non-empty timezone string** (unlike the commented-out alternate version left in the file, which would have silently fallen back instead — worth knowing the strict version is the one actually active). - **`cmd_status.py`** — `animate_processing(stop_event, stdout)`: a terminal spinner (`.`/`..`/`...`) for long-running management commands, thread-safe via a `Lock`. Used by `devtools` bulk-import commands for interactive progress feedback. --- ## 12. Testing Coverage (`core/tests/`) Four files, ~291 LOC total — modest but targeted: - `test_middleware.py` (26 lines) — only covers `ClientIPMiddleware` (X-Forwarded-For parsing + `REMOTE_ADDR` fallback). No test coverage for `TimezoneMiddleware`, `ExecutionContextMiddleware`, `APIRequestLoggingMiddleware`, `RequestIDMiddleware`, or the CSRF override logic. - `test_models.py` (107 lines) / `test_models_definitions.py` (23 lines) — cover `AuditFieldsModel`/`TimeStampedModel` behavior (soft delete/restore, manager filtering) and field definitions. - `test_utils.py` (135 lines) — covers the `utils/` helpers. **Gap worth flagging**: the domain event system (`events/`) — arguably the most architecturally important part of this app — has **no dedicated test file inside `core/tests/`**. If tests for `emit_event`, subscriber dispatch, or the persistence policy exist, they likely live in `audit`'s test suite instead (since `audit` is what actually exercises the full pipeline end-to-end). --- ## 13. Quick Reference — What To Import From Where | Need | Import | |---|---| | Soft-delete / timestamp fields on a model | `from core.models import TimeStampedModel, ActiveManager` | | Created/updated/deleted-by FKs | `from core.models import AuditFieldsModel` | | Emit a domain event from a service | `from core.events import emit_event` | | Read the current actor/request id/client ip/tz/UA | `from core.execution_context import get_actor, get_request_id, get_client_ip, get_timezone, get_user_agent` | | Mark the current context as a management command run | `from core.execution_context.helpers import set_management_command_context` | | Lazy Redis access | `from core.utils.redis_client import redis_client, safe_get, safe_setex, safe_delete` | | Validate a timezone string | `from core.utils.timezone import normalize_timezone` | --- ## 14. Summary Assessment `core` cleanly implements its stated purpose — a decoupled, fail-safe substrate for context propagation, domain events, and HTTP-layer plumbing — and several parts of it (the event severity/persistence policy, the label-resolution system, the admin-filter registry) are genuinely nice config-driven extension points that let other apps opt into infrastructure behavior by adding a dict entry rather than writing new `core` code. The main things worth the team's attention, in rough priority order, are: the two parallel context-var modules (§7.1), the orphaned/malformed `core/urls.py` (§7.2 — low risk today since it's unreachable, but should either be wired up correctly with auth or deleted), and the tighter-than-intended `core`↔`audit` coupling (§7.5) if `core` is ever meant to be audit-agnostic. ---