NUFI Docs

Console dev

Local development of the self-service developer console.

dudaji-vn/nufi-console is the developer console. Single container — Hono on Bun serves both the Vite-built React SPA and the oRPC API from one origin.

Set up

git clone https://github.com/dudaji-vn/nufi-console.git
cd nufi-console
bun install
bun run dev
# Vite at :5173, Hono at :3000. Vite proxies /rpc and /_health.

The dev server expects the rest of the platform stack to be up. Keep npuops-platform running.

Env vars

VariableDefaultPurpose
PORT3000Hono listen port
JWT_SECRET— (required)Shared with LibreChat
JWT_REFRESH_SECRET— (required)Shared with LibreChat
LITELLM_BASE_URLhttp://localhost:4000LiteLLM admin API
LITELLM_MASTER_KEY— (required)LiteLLM master key
LANGFUSE_HOSThttp://localhost:3000Langfuse base URL
LANGFUSE_PUBLIC_KEY— (required)Langfuse credentials for usage panels
LANGFUSE_SECRET_KEY— (required)Langfuse credentials for usage panels
VITE_LIBRECHAT_URLhttp://localhost:3080Build-time: inlined into SPA for the unauthorized page
DEFAULT_USER_BUDGET10USD — applied on JIT-provision
DEFAULT_BUDGET_DURATION30dRefresh interval
DEFAULT_TPM_LIMIT10000Tokens per minute
DEFAULT_RPM_LIMIT60Requests per minute
KEY_DEFAULT_DURATION90dIssued-key TTL
SERVE_DISTtrue in prodHono serves the built SPA from /dist

The easiest dev workflow: copy the secrets from npuops-platform/.env into nufi-console/.env.

Layout

nufi-console/
├── server/                       Bun + Hono + oRPC
│   ├── index.ts                  Hono app
│   ├── orpc.ts                   base oRPC instance
│   ├── middleware/auth.ts        JWT verification (LibreChat token)
│   ├── lib/
│   │   ├── litellm.ts            LiteLLM admin client
│   │   ├── jit-provision.ts      create LiteLLM user on first visit
│   │   └── serve-public.ts
│   └── router/
│       ├── index.ts              AppRouter type
│       ├── me.ts                 me.get
│       ├── keys.ts               list / create / remove / info
│       ├── usage.ts              daily / summary / byModel / byHardware
│       └── ping.ts
└── src/                          React SPA
    ├── routes/
    │   ├── __root.tsx
    │   ├── index.tsx             profile
    │   ├── keys.tsx              key management
    │   ├── usage.tsx             analytics
    │   └── unauthorized.tsx
    ├── components/
    │   ├── KeyTable.tsx
    │   ├── KeyGenerateModal.tsx
    │   ├── KeyRevealOnceModal.tsx
    │   ├── ConfirmDialog.tsx
    │   └── ui/                   shadcn primitives
    ├── lib/
    │   ├── orpc.ts               client + TanStack Query bindings
    │   ├── format.ts
    │   └── utils.ts
    └── stores/ui.ts              Zustand

Common tasks

TaskCommand
Type-checkbun run typecheck
Build SPAbun run build
Run prod serverbun run start
Regen route treebunx @tanstack/router-cli generate
Add a shadcn primitivebunx --bun shadcn@latest add <name>
Smoke testbun run smoke

Adding a new RPC procedure

  1. Add it under server/router/<resource>.ts with o.handler(...) and .input(zod) if it takes args.
  2. Re-export from server/router/index.ts.
  3. Use it on the client:
    useQuery(api.<resource>.<proc>.queryOptions(...))
    The AppRouter type carries the contract — no manual types, no fetch wrappers.

Auth model

The middleware accepts the JWT in two places:

  • Authorization: Bearer <access_token> — verified with JWT_SECRET (HS256).
  • refreshToken cookie — verified with JWT_REFRESH_SECRET (HS256).

The browser path is automatic: LibreChat sets the cookie on the parent domain, so localhost:3080 and localhost:3001 share it.

Any verification failure → 401, redirect to /unauthorized. The unauthorized page deep-links to LibreChat sign-in (the URL inlined at build time from VITE_LIBRECHAT_URL).

JIT provisioning

server/lib/jit-provision.ts runs on the first request from a new user:

  1. Read userId from the JWT.
  2. Try to fetch the LiteLLM internal-user row.
  3. If 404, create one with the deployment default budget + limits.
  4. Cache the result for the session.

The function is idempotent.

Release

CI publishes ghcr.io/dudaji-vn/nufi-console on push to develop/main and on nufi-console-v* tags. Pin a tag in the platform via NUFI_CONSOLE_TAG=v0.2.0.