djangoplay-web / Apps / DjangoPlay — core App
DocsDjangoPlay WebAppsDjangoPlay — core App

DjangoPlay — core App

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":

20 min readApplies to v1.2.2
On this page ▾
  1. 1. What core Is
  2. 2. Module Map
  3. 3. Abstract Models (core/models/)
  4. 3.1 AuditFieldsModel (models/AuditFieldsModel.py)
  5. 3.2 TimeStampedModel + ActiveManager (models/LifecycleModel.py)
  6. 4. Execution Context (core/execution_context/)
  7. 5. Domain Event System (core/events/)
  8. 5.1 The Event Contract (contracts.py)
  9. 5.2 Emitting an Event — the public API business services use
  10. 5.3 Dispatch (emitter.py, registry.py, runtime/)
  11. 5.4 Who Actually Subscribes (this happens outside core)
  12. 5.5 Deciding What Gets Persisted (policies.py)
  13. 5.6 Event Factories (factories.py)
  14. 5.7 Target Labeling & Serialization (actors.py, serializers.py)
  15. 6. Telemetry (core/telemetry/)
  16. 7. Findings Worth Flagging to the Team
  17. 7.1 Duplicate context-var modules
  18. 7.2 core/urls.py is dead code, and is malformed
  19. 7.3 CSRFOverrideMiddleware is inert; the real logic lives in a plain function
  20. 7.4 URLResolutionLoggingMiddleware self-guards, but is unused anyway
  21. 7.5 Mutable cross-app coupling from core into audit
  22. 8. Middleware — Exact Order & Role
  23. 9. APIRequestLoggingMiddleware — Traffic Analytics Feed
  24. 10. admin_filters/ — Generic Admin Autocomplete API
  25. 11. utils/ — Small Infrastructure Helpers
  26. 12. Testing Coverage (core/tests/)
  27. 13. Quick Reference — What To Import From Where
  28. 14. Summary Assessment

1. What core Is

  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

plaintext
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.ContextVars: 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 ContextVars 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.criticals 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.