Overview — what it does
InfraGuard's top navigation is a role-first information architecture (IA): it branches by role (work area) first, then unfolds into task flows within each area. Three principles:
- The menu is DB-driven, not hardcoded. Every menu node is read from the
infr_sitemaptable (app-owned, independent). Nothing is baked into Blade. - Role boundary = permission boundary. Each node's
min_req(minimumuser_level) is the access control line. A node is shown only whenuser_level >= min_req, and the same rule is enforced server-side on the route (defense in depth). - Labels always go through translation keys. On-screen text is resolved via
__()/Locales::menuLabel()for all 8 languages.
Metrics (CPU, load, 5-minute windows, …) are not top-level menu items — they live as entity tabs and filters inside each screen. The split is "who (which role) does what," not "which metric."
In InfraGuard, "domain" means only a web TLD (
aaa.bbb.com). Work split by responsibility is called "role / area." The 8 top-level menu items are those role areas.
When to use
- On first login, to learn where things live.
- To diagnose why a screen is missing (insufficient level vs. language fallback).
- When you need to add or restructure the menu and re-run the seeder (admin/dev).
- To verify what is exposed to users at a different level.
Layout
The menu sits horizontally in the top bar (infr-topbar). Left to right:
| Region | Content |
|---|---|
| Brand | InfraGuard logo (shield + monitoring pulse) → clicks to Overview (/) |
| Role menu | infr-nav — 8 role areas, hover reveals a sub-item panel |
| Right actions | mobile menu toggle · locale switcher · theme toggle · user menu |
Each top-level item renders as an MDI icon + label + (if it has children) a chevron; children appear in a dropdown panel (infr-nav__panel). When the current path matches a child href, the top-level item gets is-active (Overview is active only when the path is exactly /).
The 8 role areas
This is the top-level tree the SitemapSeeder builds. The body_class is applied to <body> when you enter an area, scoping page styles.
| # | Role area | Owner | body_class |
Min user_level |
|---|---|---|---|---|
| 1 | Overview | shared | infr-overview |
1 |
| 2 | Infrastructure | infra ops | infr-infra |
1 |
| 3 | Service | service ops | infr-service |
1 |
| 4 | Security | security ops | infr-security |
2 |
| 5 | Backup & Recovery | infra (also) | infr-backup |
2 |
| 6 | Assets | shared reference (CMDB) | infr-asset |
1 |
| 7 | Administration | admin | infr-admin |
10 |
| 8 | Development | super_admin | infr-dev |
9 |
Sub-items per area
Each sub-item carries its own min_req (usually the same as its parent, sometimes raised).
Infrastructure (min_req 1)
| Item | Path | min_req |
|---|---|---|
| Health | /infra/health |
1 |
| Servers | /infra/servers |
1 |
| Storage & RAID | /infra/storage |
1 |
| Port Connections | /infra/traffic/ports |
1 |
| IP Connections | /infra/traffic/ips |
1 |
| Traffic | /infra/traffic/net |
1 |
| Alerts | /infra/alert |
1 |
Service (min_req 1)
| Item | Path | min_req |
|---|---|---|
| Overview | /service |
1 |
| Web | /service/web |
1 |
| Database | /service/database |
1 |
| Sessions | /service/session |
1 |
| Catalog | /service/catalog |
1 |
| Projects | /service/projects |
1 |
| Problem Resolution | /service/resolution |
2 |
Security (min_req 2 — all items 2)
| Item | Path |
|---|---|
| Overview | /security |
| Threats | /security/threats |
| Detections | /security/detections |
| Rulesets | /security/rulesets |
| Firewall | /security/firewall |
| Blocklist & Reputation | /security/blocklist |
| Whitelist | /security/whitelist |
| Security Diagnostics | /security/diagnostics |
| Problem Resolution | /security/resolution |
Backup & Recovery (min_req 2): Schedule Board /backup/board · Backup Scripts /backup/scripts · Vaults /backup/vaults · Recovery & Validation /backup/recovery.
Assets (min_req 1): Inventory /asset/inventory · Instances /asset/instances · Hardware /asset/hardware · Storage & Files /asset/storage.
Administration (min_req 10 — all items 10): Users & Roles /admin/users · Collectors /admin/collectors · Operations Policy /admin/ops-policy · API Keys /admin/api-keys · Rules & Thresholds /admin/rules · Reputation API /admin/reputation-providers · AI Adapters /admin/ai-providers · Mail Adapters /admin/mail-providers · SMS Adapters /admin/sms-providers · Localization /admin/localization · Audit /admin/audit.
Development (min_req 9 — all items 9): Dev Overview /dev · Doctor /dev/doctor · Runtime & Leader /dev/leader · Slow Queries/Requests/Jobs/Outgoing · Exceptions · Cache · Servers · Usage · Pulse /pulse · Horizon /horizon.
How to use
Navigating
Hover a top-level item to open its panel. Clicking an item navigates to the screen and applies that area's body_class to <body>. On narrow screens, the top-right hamburger (data-nav-toggle) expands the menu.
What your level sees
At login, the InfrAuth middleware syncs users.user_level into the session key infr_user_level. At render, Menu::for_user($level) walks the tree and recursively hides any node whose min_req > your level. For example:
- Level 1 (demo): sees Overview · Infrastructure · Service · Assets — but not Security (2), Backup (2), Administration (10), or Development (9).
- Level 5 (staff): the above plus Security · Backup & Recovery (both 2).
- Level 10 (admin): everything, including Administration (10) and Development (9).
Development (9) and Administration (10) have different thresholds. A super_admin at exactly level 9 sees Development but not Administration. An admin (10) sees both.
Adding or restructuring the menu (admin/dev)
There is no menu-editing UI yet. The tree's source of truth is code (database/seeders/SitemapSeeder.php), and the seeder deletes all rows and recreates them on every run (canonical resync).
# After editing the tree, resync (touches infr_sitemap only — legacy data is safe)
php artisan db:seed --class=SitemapSeeder
The seeder calls Menu::flush() at the end, so changes take effect immediately. If a new item shows its raw (English) name, it lacks a translation key — add one under resources/lang/{ko,en}/menu.php using the Str::slug(name, '_') rule (e.g. Storage & RAID → storage_raid).
Never run
migrate:fresh. InfraGuard shares a connection with the legacy stats-design MySQL. To refresh only the menu, usedb:seed --class=SitemapSeederexclusively.
Data sources
The infr_sitemap table
An independent, app-owned table (distinct from the legacy unprefixed sitemap). Model: App\Models\Infr\InfrSitemap.
| Column | Meaning |
|---|---|
parent_id |
0 = top level, else parent node id |
seq |
sibling sort order |
depth |
0 = role area, 1 = sub-item |
name |
raw name (source of the label key) |
href |
link path (# = non-linking parent) |
icon |
MDI glyph (e.g. mdi-server) |
body_class |
top-level only — <body> class on entry |
is_view |
1 to include in the tree |
min_req |
minimum user_level (permission boundary) |
App\Support\Menu — tree builder + cache
Menu::tree()— readsis_view=1rows ordered byseq,id, assembles a recursive tree, and caches it withrememberForever(keyinfr:menu:v2). If Redis is unavailable, it falls back to an uncachedbuild().Menu::for_user($level)— recursively filters the tree bymin_req > $level.Menu::flush()— invalidates the cache (called by the seeder).
The cache stores the raw
name; translation happens at render time viaLocales::menuLabel(). So switching languages requires no cache flush. Conversely, adding or renaming nodes requires a re-seed (which flushes).
Label translation
App\Support\Locales::menuLabel($name) looks up the key menu. + Str::slug($name, '_'). If the key exists it translates, otherwise it returns the raw name (safe fallback). Packs live in resources/lang/{locale}/menu.php.
// In the render (nav.blade.php)
{{ \App\Support\Locales::menuLabel($top['name']) }}
// "Storage & RAID" → __('menu.storage_raid') → "Storage & RAID" (en) / "스토리지 · RAID" (ko)
Session and middleware
InfrAuth— on authentication, syncsusers.user_level→ sessioninfr_user_level. This is the input to the menu filter.InfrLevel(infr.level:N) — route gate. Returns 403 ifinfr_user_level < N(server-side enforcement, separate from menu hiding).InfrAdmin/InfrDev— protect the Administration (≥10) and Development (≥9) route groups.
Access
Level (user_level) |
Alias | Visible in the menu |
|---|---|---|
| 1 | demo/viewer | Overview · Infrastructure · Service · Assets (mostly read) |
| 5 | staff | + Security · Backup & Recovery |
| 9 | super_admin | + Development |
| 10 | admin | + Administration (and Development) |
- Menu hiding is a convenience and a first line of defense only. The real control is enforced server-side by per-route
infr.level:N/InfrAdmin/InfrDevmiddleware (typing the URL directly still yields 403). - Destructive/security actions (blocking, restore, rule changes, …) are usually re-gated inside each screen by a separate write level (e.g. write ≥2, admin ≥10) and recorded to the audit log.
For level definitions see RBAC levels; for role/team management see Roles & Teams.
Troubleshooting & FAQ
A menu item is missing.
Usually an insufficient level. Check that your user_level is ≥ that node's min_req (see tables above). Admins adjust levels in Users & Teams.
A newly added menu item shows its English (raw) name.
The menu.php pack lacks a translation key. Add one to both resources/lang/ko/menu.php and en/menu.php using the Str::slug(name,'_') rule. The screen still works safely with the raw name if the key is absent.
I ran the seeder but the menu didn't change.
Stale cache is possible. The seeder calls Menu::flush() at the end, but you can also try php artisan cache:clear. If Redis is down, Menu falls back to build() per request, so caching cannot be the culprit.
I switched languages but only the menu labels didn't change.
Labels are resolved at render time, not cached. Verify the target language's menu.php has the key. If not, check activation/translation status on the Localization page.
Can someone reach a hidden screen by typing the URL?
No. Even when the menu is hidden, the route middleware (infr.level:N, etc.) returns 403 server-side.
Related
- RBAC levels — how
user_levelandmin_reqare evaluated - Roles & Teams — roles, teams, and owners
- Localization — language switching and fallback
- Theme — light/dark tokens
- Users & Teams (admin) — adjusting levels
- Overview (landing) — the unified-health home screen

