Architecture overview¶
The 5-minute mental model of the running application.
Runtime layers¶
Browser
React 19 + TanStack Start/Router/Query/Table + Tailwind 4
frontend/src/routes/ and frontend/src/components/
|
v
nginx (one public origin)
SPA/SSR | /api/ | /ws/ | /admin/ | static/media | docs
| |
v v
gunicorn daphne
\ /
Django 5.2 + DRF
tenant/site RBAC, validation, audit
|
PostgreSQL + Redis/RQ workers
The React application in frontend/ is the only product frontend. Django owns
the JSON API, admin, background work, static/media handling, and WebSockets. It
does not provide a parallel server-rendered product UI. In development, Vite on
port 3000 is the user-facing server and proxies Django routes to port 8000.
Production deliberately separates normal HTTP traffic (gunicorn) from WebSockets (daphne). nginx presents both, plus the frontend and documentation, through one origin.
Ownership boundaries¶
| Area | Primary code |
|---|---|
| React routes and page workflows | frontend/src/routes/ |
| Shared UI and tables | frontend/src/components/ |
| Typed frontend API boundary | frontend/src/lib/api.ts |
| IPAM, DCIM, connectivity, and topology | api/ |
| Tenant settings and shared platform behavior | core/ |
| Authentication, RBAC, API tokens, and LDAP | auth_api/ |
| Checks, alerts, engines, and SNMP | monitoring/ |
| Webhooks, automation, drift, and imports | integrations/ |
| Change history and journal | audit/ |
| Background execution | jobs/ and RQ workers |
Request lifecycle¶
For a typical list page:
- TanStack Router mounts the route and TanStack Query requests
/api/.... - Django resolves the authenticated user and active tenant.
- The viewset scopes its base queryset to that tenant, then applies RBAC and site/constraint filters before any user-controlled identifier or filter.
- DRF serializes the authorized rows and returns JSON.
- TanStack Query owns the client cache; shared table and page-shell components render loading, error, empty, and populated states.
Tenant isolation is a server-side security boundary. Client-side route guards, hidden controls, and picker filtering improve usability but never replace queryset and relationship validation in Django.
Data principles¶
Danbyte does not seed illustrative inventory. Example devices, sites, prefixes, VLANs, and tags belong only in opt-in demo seeders. Minimal deterministic data needed for invariants, access control, protocol constants, or a safe first run may be created by bootstrap or forward data migrations. See the repository agent guide for the complete seed classification rules.
PostgreSQL is required. Domain objects use UUID primary keys, and migrations are forward-only because public-preview installations already hold real data.
Repository layout¶
danbyte/
danbyte/ # Django settings, URLs, WSGI, ASGI
api/ # IPAM/DCIM/connectivity API and models
auth_api/ # authentication and RBAC
core/ # tenant settings and shared platform behavior
monitoring/ # monitoring, alerts, engines, and SNMP
integrations/ # external systems and automation
audit/ # change history and journal
frontend/ # active React/TanStack application
services/ # systemd user units and timers
deploy/ # production nginx and deployment templates
docs/ # documentation source
reference/ # archived mockups and historical reference material
frontend/src/routeTree.gen.ts and build output are generated. Archived static
mockups under reference/ are not runtime sources of truth.
Route-file conventions¶
Two rules hold for every entity under frontend/src/routes/, enforced by
frontend/src/routes/-route-shape.test.ts (the leading - is TanStack Router's
ignore prefix, so the test does not become a route):
- An entity you can create or edit must be openable. If
<entity>.new.tsxor an edit route exists,<entity>.$id.tsxmust exist too. Without it the only way to reach a record is the edit form, and any History the object is already accumulating has nowhere to render. Non-object screens (settings,import,topology) and the RBAC admin lists (users,groups,permissions) are allow-listed; entities that still lack a detail page are named in an acknowledged-gap list that is asserted to match exactly. - Edit routes are named
$id_.edit, never$id.edit. The file router treats<entity>.$id.tsxas the layout parent of<entity>.$id.edit.tsx, so a dot-named edit route renders the detail component instead of the form — and the detail component has no<Outlet/>, so the form never appears. It typechecks and it builds. The trailing underscore opts the child out of the nesting; the URL is unchanged.