Skip to content
Daniel Hokanson edited this page Aug 30, 2026 · 2 revisions

This wiki is for people working inside the Forge frontend: how the Angular application is laid out, what the build actually produces, which conventions are mechanically enforced and which are only prose, and where the traps are. It assumes you already know what Forge is. If you don't, start at the Forge wiki — product concepts, capability gating, roles, accounting modes and installation all live there and are deliberately not repeated here.

What this repo owns

forge-ui is the whole browser client and nothing else. One Angular application, built four ways, serving every human-facing surface Forge has: the desktop employee shell, the phone web UI, the native shell, the shop-floor kiosk, the customer portal and the anonymous order-acceptance page. In production it ships as an nginx image that serves the compiled bundle and reverse-proxies /api/, /hubs/ and the signing service to their containers, so the SPA and the API are same-origin from the browser's point of view.

It holds no data. Every entity, permission decision and capability refusal is the API's; the SPA renders them and re-asks. Two consequences worth internalising before you change anything:

  • A guard in this repo is a courtesy, not a control. Route guards, the nav filter and the capability interceptor exist to avoid rendering doors that lead nowhere and to keep devtools quiet. The server refuses independently, and a client that ignores every guard here gets exactly the same refusals — see API Access on the hub.
  • The HTTP contract is not generated. Service methods, URL strings and response interfaces are written by hand against forge-api. Nothing in the type system notices when a route moves. What notices is a test — see Testing § Contract drift.

What it does not own

Concern Lives in
Endpoints, authorization, capability enforcement forge-api
Database schema and migrations forge-db
Compose files, .env, image builds, upgrade orchestration forge-deploy
Public demo SPA and the manual test plans forge-test
Telephony forge-voice

The Dockerfile here builds the production image, but it is not how Forge is deployed — forge-deploy pulls the published image, owns compose and runs the upgrade. Building the image locally is a debugging move, not a release step.

Working on it

The inner loop — bringing a stack up, pointing the dev server at an API, and the environment-file gotcha that catches everyone once — is on the hub at Developer Setup. What is repo-specific is the gate. The pull-request workflow runs five things in order, and they are worth running locally in the same order because each one catches a class the previous cannot see:

npm run lint          # ESLint + @angular-eslint, under a pinned warning ceiling
npm run lint:i18n     # translation keys + en/es parity
npm run lint:standards # the conventions ESLint can't express, with a per-file ratchet
npm run build         # production configuration — AOT, strict templates, budgets
npm run test -- --watch=false   # Vitest on jsdom

Playwright is not in that list. It runs nightly against a real stack and assumes one is already up.

Where to go next

Page What it covers
Project Layout shared/ vs features/, standalone + signals + zoneless, and how close shared/ is to being liftable
The Surfaces The routing tree and the four build configurations behind the hub's product-level surface map
Standards and Ratchets Hard rules, the per-file baseline, regenerating it, the warning ceiling, and why the build is its own gate
i18n Two catalogs, enforced parity, the static key scanner, and the assets path that fails silently
Testing Vitest on jsdom, Playwright against a running stack, and which projects gate

On the hub: App Surfaces for what each entry point is for, Mobile and Offline for the native shell and what survives a network drop, Customizing an Install for the runtime label/branding layer that sits above i18n, and Capability Gating for why a menu item is missing.

Clone this wiki locally