ADR 0004: First web interface and shared contracts
- Status: Accepted for the local experiment
- Date: 2026-10-29
Context
Section titled “Context”The backend experiment needs a browser interface that exercises real PostgreSQL records and the same operations available to API and MCP clients. This ADR records the first interface slice. The current local permission model is implemented under ADR 0027; full product acceptance and domain modules remain outstanding.
Decision
Section titled “Decision”Use React and Vite with TypeScript, TanStack Query for server state and i18next for interface strings. Compose standard Base UI-based shadcn/ui components (base-nova) with Tailwind and centralized semantic CSS variables. Keep business data on the server; only presentation preferences such as browser theme and account/view-scoped column visibility are stored locally; never business contents or credentials.
Serve the built browser assets through Fastify on the same origin as Better Auth, API and MCP. Local iteration uses Vite build watch and browser refresh. Preserve existing Host/Origin checks rather than introducing a cross-origin development exception.
Move canonical operation input schemas and wire types into packages/contracts. Add object listing and bounded, literal Keyword search to the existing Application so UI, HTTP and MCP share authorization and behavior. The first slice used Administrator-only scalar-text records; ADR 0007 and ADR 0026 subsequently add typed fields and content grants. Those later decisions govern current behavior.
The shell follows the official Sidebar 07 structure; record lists use the Tasks example as a layout reference. Use upstream component styles, variants and theme variables. The initial Radix experiment was switched to Base UI before merging, following the selected common component baseline. Preserve localized labels and unsaved-edit protection when updating generated primitives.
React Router in data mode owns browser history and URL state. Personal settings use /settings; workspace Dashboards use /w/:code; object lists use /w/:code/objects/:technicalName, with an optional record UUID or new suffix. Search, sorting and pagination use query parameters. Route blocking preserves the existing discard dialog for internal navigation and Back/Forward; document reload/exit uses the native before-unload guard. The server serves the application shell for extensionless browser document routes, including unknown pages handled by the client error view. API/MCP paths, asset paths, file-like paths and non-HTML/non-GET requests retain their error responses.
Consequences
Section titled “Consequences”The first interface supports authentication, workspace/object/text-field creation, paginated record tables, editing and record journals. Version checks remain authoritative in the backend; the client guards unsaved edits. German/English labels use the existing stored preferences and definition-language fallback.
The server can run without built browser assets for backend-only development. Full version-1 acceptance, production packaging, domain modules, full permission acceptance and production agent authorization remain separate work; ADR 0027 defines the implemented local permission model. UI assets have to be built before browser use; no independent Vite development origin or live reload is provided by this experiment.