Skip to content

ADR-004: hermesx-webui Vite Multi-Page Architecture

Decision Info

Field Value
Number ADR-004
Title Vite Multi-Page: User Portal (index.html) + Admin Console (admin.html)
Status Accepted
Date 2026-05-08
Owner architect
Related Requirement docs/artifacts/2026-05-08-hermesx-webui/prd.md

Background and Constraints

  • The PRD requires Admin Console (/admin/) and User Agent Portal (/) to coexist in the same repository.
  • The challenge session architect recommended Vite multi-page over single SPA + route guards for the following reasons:
  • The two portals have different authentication mechanisms (user key vs admin key)
  • Independent bundles prevent admin dependencies from contaminating the user bundle
  • Aligns with the existing backend /static/ serving pattern (index.html + admin.html)
  • Non-goal: Not splitting into two separate npm projects (maintenance cost too high).

Alternatives

A: Single SPA + Route Guards (Rejected)

All pages in the same Vue application, distinguishing permissions via /admin/* routes and route guards.

Rejected because: - Admin and User bundles cannot be separated; admin dependencies (e.g., large Audit Log tables) contaminate the user bundle - Route guards only guard at the frontend, no bundle-level isolation - Single SPA requires runtime detection of "currently admin or user", high coupling

B: Vite Multi-Page (Adopted)

Two independent HTML entry points:

webui/
├── index.html          ← User Portal entry (built as /index.html)
├── admin.html          ← Admin Console entry (built as /admin.html or /admin/index.html)
├── src/
│   ├── user/
│   │   ├── main.ts     ← User Portal app instance
│   │   ├── App.vue
│   │   └── router.ts
│   ├── admin/
│   │   ├── main.ts     ← Admin Console app instance
│   │   ├── App.vue
│   │   └── router.ts
│   ├── shared/         ← Code shared between both entries
│   │   ├── api/        ← useApi.ts, useSse.ts
│   │   ├── stores/     ← auth.ts (Pinia, instantiated separately)
│   │   ├── types/      ← TypeScript types
│   │   └── components/ ← Shared UI components
│   └── pages/          ← Page components (split into user/ and admin/ subdirectories)
└── vite.config.ts      ← build.rollupOptions.input configured for two entries

Key vite.config.ts configuration:

build: {
  rollupOptions: {
    input: {
      main: resolve(__dirname, 'index.html'),
      admin: resolve(__dirname, 'admin.html'),
    },
  },
}

Advantages: - Independent bundles — User Portal loads no Admin code - Natural permission isolation: admin.html loads the admin app instance, never mixed with user - Clear nginx routing: /adminadmin.html, /index.html - Fully compatible with the existing backend /static/ serving pattern

Decision Outcome

Adopting Option B: Vite Multi-Page.

Nginx routing configuration:

location /admin {
    try_files $uri $uri/ /admin.html;
}
location / {
    try_files $uri $uri/ /index.html;
}

Directory migration plan (Phase 0): 1. Reorganize existing src/ content into src/shared/ + src/user/ + src/admin/ 2. Create admin.html entry point 3. Update vite.config.ts to multi-page mode

Follow-up Actions

Action Owner Completion Criteria
Reorganize directory structure, configure vite.config.ts frontend-engineer Phase 0
Update nginx.conf (SSE configuration see ADR-006) frontend-engineer Phase 0
Verify npm run build produces two HTML files qa-engineer Phase 0 acceptance