Web Frontend Component
The Web Frontend Component adds server-rendered pages to your project: Jinja2 templates, htmx for interactivity, Alpine.js for client-side state, styled with Tailwind CSS and DaisyUI.
It is additive. The Flet frontend is core and always present; selecting
htmx puts a second, HTML-first surface beside it, served by the same
webserver process:
one webserver process
┌────────────────────────────────────────────────┐
│ │
│ / htmx pages (Jinja2 templates) │
│ /dashboard Flet Overseer dashboard │
│ /overseer server-rendered Overseer page │
│ /api/v1/... FastAPI routes │
│ /static/... fingerprinted assets │
│ /health component health │
│ │
└────────────────────────────────────────────────┘
Use the Flet dashboard for operator surfaces and internal tooling; use the web frontend for anything public-facing, content-heavy, or SEO-relevant. Both call the same services, so business logic is written once.
Getting it
On a new project:
On an existing project:
Either way you get a styled landing page at /, the Overseer dashboard
untouched at /dashboard, and a web_frontend entry in /health. Removal is
symmetric: aegis remove htmx deletes the component tree and regenerates the
shared files that referenced it.
Overseer comes with it at /overseer: every component and service as a list,
cards or a map of the stack, each linking to its own page. The map's Traffic
chip tones each node by how much ran through it instead of its health:
every call along the lines touching it, Overseer's own reads included,
counted as it happens with no tracer needed. Opened up, each service that
breaks the service standard says how many places it should fix. To sign in
to it, and keep it to admins, add auth:
Then open /overseer/login. The form uses the shared auth and form macros and
delegates to the existing auth endpoint.
Who can open the Overseer
The Overseer is operator access to the deployment: it shows its routes,
source and live data, and runs user administration, scheduler jobs and model
loads. Without the auth service it is open to read: whoever reaches the app
may use it, like any route a stack serves without auth. Every write it makes
(Restart, Secrets, a refund, a test send, an upload, a saved edit) is made
only on the local dev build (BUILD_ID not yet stamped by aegis deploy),
opened at localhost, 127.0.0.1 or [::1]; anywhere else it is refused
and its controls hidden, with a line saying why. With auth, every Overseer page, live stream and action is open
to admins only, not to every signed-in account. An admin is:
- an account whose email is in
ADMIN_USER_EMAILS, or - an account with the
adminrole, when the auth service runs at the RBAC level (auth[rbac]).
With AUTH_ENABLED=false (local development) the Overseer opens without
signing in: a request with no session resolves to the dev user, an admin.
Set the allowlist in .env as a JSON list, then restart the server:
With auth on and an empty allowlist, nobody reaches the Overseer: a signed-in
account that is not an admin gets an "Admins only" page saying how to get in,
and its fragments and streams answer 403. Signed out, Overseer pages redirect
to /overseer/login. The check is one dependency on the pages router
(overseer_access.py, the one module that knows whether the stack has auth),
and tests/web/test_overseer_admin_gate.py walks every Overseer route as a
non-admin, so a new page cannot skip it; without auth,
tests/web/test_overseer_open.py walks them signed out, and nothing refuses. require_admin,
for API routes you protect yourself, uses the same definition (is_admin); the
API's built-in operator routes keep their own checks.
Basic auth has no email verification step. New registrations are ready
immediately and the registration form redirects to /overseer. Email based
password reset is also absent from basic auth because this configuration has
no mail delivery; signed-in password changes remain available.
The shared feedback, form, layout, and table macro files were compared
with aegis-pulse and aegis-steward before adding this page. Pulse's shared
feedback, form, and table macros already match the template. The generic
updates brought in from those projects are the dialog behavior, stable search
field ID, and empty-state marker. Steward's account, bill, matter, import,
register, and chat macros depend on its own routes and data, so they remain
in that application.
What ships
app/components/web_frontend/
├── main.py # router wiring
├── rendering.py # Jinja2 env, render() (page or fragment), with_toast()
├── assets.py # static() helper, CachedStaticFiles
├── filters.py # money / short_date / pct
├── build.py # asset fingerprinting -> static/dist/manifest.json
├── build_watch.py # dev watcher: re-fingerprint on change
├── routes/
│ ├── pages.py # page handlers (the landing, auth pages)
│ └── partials/ # fragment-only routes
├── templates/
│ ├── base.html # document skeleton every page extends
│ ├── layouts/ # page.html and fragment.html, picked by render()
│ ├── pages/ # one template per page
│ └── components/ # macros, feedback macros, landing sections
└── static/
├── input.css # Tailwind entry
├── css/app.css # hand-authored styles
└── js/app.js # toasts and htmx error hooks
package.json # Tailwind + DaisyUI + Biome (devDependencies only)
tailwind.config.js # theme + the single rebrand point
Plus gated tests (tests/components/test_web_frontend.py and the web test
kit under tests/web/), Makefile
targets (build-static, lint-frontend, format-frontend), a Docker
css-build stage, and two dev-only compose services for hot CSS rebuild.
First custom page
-
Add a template:
-
Add a route in
app/components/web_frontend/routes/pages.py:
That is the whole loop. In dev no build step is required: static() falls
back to unhashed asset paths and pages render immediately. Run
make build-static when you want the fingerprinted, long-cacheable variant.
The rest of these docs
- Templates and rendering: the base layout, the htmx conventions, and two rules that are load-bearing.
- Styling and theming: the DaisyUI theme and the one place brand colors live.
- Live updates: snapshots, SSE, and shared health sampling.
- Asset pipeline: fingerprinting, the manifest, cache policy, and the dev watcher loop.
- Auth pages: what you get when the project also selects the auth service.