This document provides comprehensive guidance for AI assistants working on this codebase.
Ory Admin UI is a production-grade single-page application for managing identities and access control in Ory Kratos identity servers. It provides an intuitive interface for CRUD operations on identities, sessions, courier messages, and identity schemas.
Key Features:
- Identity management (create, read, update, delete)
- Session management and revocation
- Courier message viewing
- Identity schema browsing
- Dark/light theme support
- Responsive design
| Category | Technology |
|---|---|
| Framework | Vue 3.5 (Composition API with <script setup>) |
| Language | TypeScript 5.7 (strict mode) |
| Build Tool | Vite 6 |
| Package Manager | Bun (primary) or npm |
| State Management | Pinia |
| Data Fetching | TanStack Vue Query |
| HTTP Client | ky |
| Styling | Tailwind CSS 3.4 |
| UI Components | Radix Vue primitives |
| Icons | Lucide Vue Next |
| Notifications | vue-sonner |
| Linting | oxlint, ESLint |
| Formatting | Prettier |
| Testing | Vitest |
src/
├── api/ # API layer - HTTP clients for Ory Kratos
│ ├── client.ts # Base ky client configuration
│ ├── identities.ts # Identity CRUD operations
│ ├── sessions.ts # Session management
│ ├── courier.ts # Courier message API
│ ├── schemas.ts # Identity schemas API
│ └── health.ts # Health check endpoints
├── assets/
│ └── styles/
│ └── main.css # Global styles and Tailwind imports
├── components/
│ ├── common/ # Reusable utility components
│ │ ├── CopyButton.vue
│ │ ├── EmptyState.vue
│ │ ├── ErrorState.vue
│ │ ├── JsonViewer.vue
│ │ ├── LoadingSpinner.vue
│ │ ├── Pagination.vue
│ │ ├── StatusBadge.vue
│ │ └── TimeAgo.vue
│ ├── layout/ # App shell components
│ │ ├── AppFooter.vue
│ │ ├── AppHeader.vue
│ │ ├── AppShell.vue
│ │ └── AppSidebar.vue
│ └── ui/ # Base UI primitives (shadcn-vue style)
│ ├── Button.vue
│ ├── Card.vue, CardContent.vue, CardHeader.vue, etc.
│ ├── Dialog.vue
│ ├── Input.vue
│ ├── Select.vue
│ ├── Tabs.vue
│ └── ...
├── composables/ # Vue composables for data fetching
│ ├── useCourier.ts
│ ├── useHealth.ts
│ ├── useIdentities.ts
│ ├── useSchemas.ts
│ └── useSessions.ts
├── lib/
│ └── utils.ts # Utility functions (cn, formatDate, etc.)
├── router/
│ └── index.ts # Vue Router configuration
├── stores/ # Pinia stores
│ ├── settings.ts # API endpoint configuration
│ ├── theme.ts # Theme (dark/light/system)
│ └── ui.ts # UI state (sidebar, modals)
├── types/
│ └── api.ts # TypeScript interfaces for API models
├── views/ # Route-level page components
│ ├── DashboardView.vue
│ ├── IdentitiesView.vue
│ ├── IdentityDetailView.vue
│ ├── IdentityCreateView.vue
│ ├── SessionsView.vue
│ ├── SessionDetailView.vue
│ ├── CourierView.vue
│ ├── SchemasView.vue
│ ├── SettingsView.vue
│ └── NotFoundView.vue
├── App.vue # Root component
├── main.ts # Application entry point
└── vite-env.d.ts # Vite type declarations
# Install dependencies
bun install # or: npm install
# Development server (default: http://localhost:5173)
bun run dev # or: npm run dev
# Type checking
bun run typecheck # or: npm run typecheck
# Build for production
bun run build # or: npm run build
# Preview production build
bun run preview # or: npm run preview
# Linting
bun run lint # oxlint
# Formatting
bun run format # Prettier write
bun run format:check # Prettier check
# Testing
bun run test # Vitest watch mode
bun run test:coverage # With coveragejust lint # Run pre-commit hooks
just serve # Start dev server
just build # Production build
just mkdocs # Serve documentationCreate .env file based on .env.example:
VITE_DEFAULT_API_ENDPOINT=http://localhost:4434 # Kratos Admin API URLThe API endpoint can also be configured at runtime in the Settings page.
-
Composition API with
<script setup>- All components use the modern Vue 3 syntax:<script setup lang="ts"> import { ref, computed } from "vue" // Component logic here </script>
-
Props with TypeScript interfaces:
interface Props { variant?: "default" | "destructive" disabled?: boolean } const props = withDefaults(defineProps<Props>(), { variant: "default", disabled: false, })
-
Class Variance Authority (CVA) for component variants:
const buttonVariants = cva("base-classes", { variants: { variant: { default: "...", destructive: "..." }, size: { sm: "...", default: "...", lg: "..." }, }, defaultVariants: { variant: "default", size: "default" }, })
Uses TanStack Vue Query for server state management:
// In composables/useIdentities.ts
export function useIdentities(params?: Ref<PaginationParams>) {
return useQuery({
queryKey: ["identities", params],
queryFn: () => identitiesApi.list(params?.value),
staleTime: 30_000,
})
}
export function useCreateIdentity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (body: CreateIdentityBody) => identitiesApi.create(body),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["identities"] })
toast.success("Identity created successfully")
},
onError: (error: Error) => {
toast.error(`Failed to create identity: ${error.message}`)
},
})
}HTTP requests use ky with centralized configuration:
// In api/client.ts
export function createApiClient() {
const settings = useSettingsStore()
return ky.create({
prefixUrl: settings.kratosAdminBaseURL,
timeout: 30000,
hooks: {
/* logging hooks */
},
})
}Pinia stores use the setup syntax:
export const useThemeStore = defineStore("theme", () => {
const theme = ref<ThemeMode>("dark")
const isDark = computed(() => theme.value === "dark")
function setTheme(newTheme: ThemeMode) {
/* ... */
}
return { theme, isDark, setTheme }
})- Strict mode enabled - No implicit any, strict null checks
- Path aliases - Use
@/forsrc/imports - Interface naming - Use PascalCase (e.g.,
Identity,CreateIdentityBody) - Type exports - Export types from
src/types/api.ts
- File naming - PascalCase for components (e.g.,
IdentityDetailView.vue) - Component structure:
<script setup lang="ts">block first<template>block second<style>block last (if needed, scoped)
- Emit typing - Use typed emits with
defineEmits<{...}>()
- Tailwind CSS - Use utility classes exclusively
- Custom colors - Defined in
tailwind.config.js:surface,surface-raised,surface-overlay- Background colorstext-primary,text-secondary,text-muted- Text colorsaccent,accent-hover- Accent colors (cyan)border,border-subtle,border-focus- Border colorssuccess,warning,destructive- Status colors
- Dark mode - Class-based (
darkMode: 'class'), dark is default - cn() utility - Use for conditional class merging:
import { cn } from "@/lib/utils" const classes = cn("base-class", props.class, { "conditional-class": condition })
- Lazy loading - All routes use dynamic imports
- Named routes - Always use
nameproperty for navigation - Nested routes - AppShell wraps all main routes
Tests use Vitest with Vue Test Utils:
bun run test # Watch mode
bun run test:coverage # Coverage reportTest files should be placed alongside source files or in __tests__/ directories.
# Build image
docker build -t ory-admin-ui .
# Run container
docker run -p 8080:8080 ory-admin-uiThe Dockerfile uses:
oven/bun:1for buildingstatic-web-server:2for serving (lightweight, ~2MB)
GitHub Actions workflow (.github/workflows/ci.yml):
- Builds Docker images on push to main and PRs
- Uses cosign for image signing
- Kubescape security scanning
- Release Please for automated releases
- Images pushed to Docker Hub and GHCR
| File | Purpose |
|---|---|
src/main.ts |
App initialization (Pinia, Router, Vue Query) |
src/router/index.ts |
Route definitions |
src/api/client.ts |
HTTP client factory |
src/types/api.ts |
API type definitions |
src/lib/utils.ts |
Utility functions |
src/stores/settings.ts |
API endpoint configuration |
src/stores/theme.ts |
Theme management |
tailwind.config.js |
Tailwind customization |
vite.config.ts |
Vite build configuration |
tsconfig.json |
TypeScript configuration |
- Add types to
src/types/api.ts - Create API functions in
src/api/(e.g.,src/api/newFeature.ts) - Create composable in
src/composables/using Vue Query - Use composable in view components
- Create view component in
src/views/(e.g.,NewFeatureView.vue) - Add route in
src/router/index.ts - Add navigation link in
src/components/layout/AppSidebar.vue
- Create component in
src/components/ui/ - Use CVA for variants if needed
- Follow existing component patterns (props interface, cn() utility)
- Edit colors in
tailwind.config.js - Update CSS variables in
src/assets/styles/main.cssif needed - Theme toggle is handled by
useThemeStore
The project uses pre-commit with:
- Standard hooks (trailing whitespace, file size, JSON formatting)
- Commitlint (conventional commits required)
- ESLint with auto-fix
Run manually: pre-commit run -a or just lint
- Always use TypeScript - The codebase is strictly typed
- Prefer Composition API - Don't use Options API
- Use existing patterns - Follow the established composable and component patterns
- Tailwind only - Don't add custom CSS unless absolutely necessary
- Keep it simple - This is an admin UI, prioritize functionality over complexity
- Test changes - Run
bun run typecheckandbun run lintbefore committing - Conventional commits - Use prefixes like
feat:,fix:,refactor:, etc.