Skip to content

Frontend Developer Onboarding Guide

Welcome to the Frontend

Web frontend for the Xavier platform is called AI Portal or Studio — a creative-production workspace that includes projects with a Storycraft section, shot boards, editorial timeline along with an AI chat copilot.

Xavier Frontend AI Portal

The onboarding path is divided into stages, and each stage should leave a visible result: a complete repository, configured environment, working dev-service connection, locally running Studio, and a small validated change.

Craftology Frontend Onboarding PathCraftology Frontend Onboarding Path1. Access & repository2. Local configuration3. Runtime environment4. First contributionAccess grantedGitLab, LDAP, Vault,registry, dev clusterClone AI PortalInitialize xavier.grpcsubmoduleInstall dependenciespnpm installPull DEV .envfrom VaultLocal config readyConnect with Telepresenceto dev clusterStart dev serverpnpm devStudio runs locallyOpen Studio and inspect a real screen.Make a safe visual change.Validate in browser and responsive views.Open MR.Review and merge.xavier.grpc is required.Without it, gRPC generationcan fail during install.The Vault command can overwrite .env.Save local edits before rerunning it.VPN and kubeconfig must be readybefore Telepresence can connect.
Craftology Frontend Onboarding PathCraftology Frontend Onboarding Path1. Access & repository2. Local configuration3. Runtime environment4. First contributionAccess grantedGitLab, LDAP, Vault,registry, dev clusterClone AI PortalInitialize xavier.grpcsubmoduleInstall dependenciespnpm installPull DEV .envfrom VaultLocal config readyConnect with Telepresenceto dev clusterStart dev serverpnpm devStudio runs locallyOpen Studio and inspect a real screen.Make a safe visual change.Validate in browser and responsive views.Open MR.Review and merge.xavier.grpc is required.Without it, gRPC generationcan fail during install.The Vault command can overwrite .env.Save local edits before rerunning it.VPN and kubeconfig must be readybefore Telepresence can connect.

App and Repository Overview

The main active app is Studio: a creative-production workspace where users work with projects, conversations, cards, shots boards, Storycraft flows, editorial timelines, files, and an AI chat copilot.

The app is built with Next.js 16, React 19, and TypeScript. Most day-to-day frontend work feels like client-side React development, while Next.js also provides the application routing and server-side API layer. The project uses MUI 7 and an internal Aurora UI kit, so developers may encounter more than one design style depending on which part of the product they are changing.

The repository is a single main frontend codebase rather than a set of micro-frontends. It has grown through several phases, so not every feature follows the same pattern.

The app uses a Backend-for-Frontend (BFF) approach. The browser does not talk directly to private backend services and does not receive backend JWTs. Instead, client components call local API routes, and those routes communicate with services such as Pocketbase, PolicyVault gRPC, Temporal, , and private file storage.

Authentication is session-based. Users log in through Keycloak, the server manages the session with an HttpOnly cookie, and session data is stored in Redis. From the frontend perspective, this means developers should use the existing session and auth helpers instead of handling tokens manually.

Data management is split by purpose. Server data is handled through TanStack Query, usually with reusable hooks. UI-only state is handled with Zustand stores. Forms use React Hook Form and Zod. This separation is important because it keeps loaded backend data, temporary UI state, and form validation concerns from being mixed together.

The repository also depends on a required gRPC submodule, xavier.grpc. It contains shared .proto service contracts used to generate TypeScript gRPC stubs. A frontend developer usually does not edit these contracts directly, but they need to know that the submodule exists because installation and service integration depend on it.

For local development, the app expects access to internal services. Environment variables are pulled from Vault with LDAP credentials, and Telepresence is used to let the locally running app reach backend services in the dev Kubernetes cluster. This is why a local setup issue may be caused by missing access, expired credentials, missing kubeconfig, or VPN problems rather than by frontend code.

Get Access Before You Start

Before a new frontend developer tries to run Craftology locally, it is worth making sure that all access is already in place. A missing permission can look like a broken installation, a bad dependency, or a frontend bug, when in reality the developer simply cannot reach one of the internal systems the app depends on.

Gitlab Repository

The first thing you need is access to the main frontend repository and its submodules. The frontend is kept in a single main repository, but it includes the xavier.grpc submodule, which contains shared .proto contracts used to generate gRPC-related frontend code. This submodule is not optional: if it is missing, dependency installation can fail during the prepare step, because the project tries to generate gRPC code from those contracts. Git access must include both the repository itself and its submodules.

Vault

The next important access area is environment configuration. Local development depends on values stored in Vault, and the standard way to pull them is through the pnpm config:ldap-dev command. That command asks for the developer’s LDAP credentials and writes the DEV environment values into the local .env file. If the command fails, the likely issue is not the frontend code, but missing LDAP/Vault permissions for the Xavier/AI.Portal path. In that case, you should ask #helpdesk to verify the account and permissions before continuing.

npm Registry

The developer also needs access to the private ICVR npm registry. Some project packages come from the internal registry, so the local machine must be configured to resolve @icvr packages before installing dependencies. Without this setup, pnpm install may fail even if the repository itself was cloned correctly.

Kubernetes Cluster

Cluster access is another part of the setup that should be arranged early. The frontend does not run as a completely isolated mock application: its BFF routes talk to backend services, including gRPC services, PocketBase, and other upstream systems. For local development, the developer needs a kubeconfig for the dev Kubernetes cluster and a working Telepresence setup. The local setup guide says to request the kubeconfig from #helpdesk, export it through KUBECONFIG, connect with Telepresence, and then verify the connection with telepresence status and kubectl get ns. Note that VPN must be enabled for this environment to work properly.

Pocketbase

Finally, the developer should have access to the supporting development systems that are needed to see realistic data and permissions in the product. PocketBase access is important because the frontend reads application data from it, and some development tasks may require checking or adjusting fields there. Such changes should not be made casually: PocketBase field changes should be coordinated with the lead or responsible person and announced in the shared thread so that environments remain synchronized.

Prepare Local Toolchain

  1. Install Node.js - the current LTS release (20.x or newer) for local development.
  2. Use pnpm only to install dependencies; do not install dependencies with npm or yarn. Enable Corepack and prepare pnpm@10.28.1, which matches the package manager pinned in package.json.

    corepack enable
    corepack prepare pnpm@10.28.1 --activate
    

    If corepack is not found, install via npm:

    npm install -g pnpm
    
  3. Specify a local npm registry:

    npm config set @icvr:registry https://pkg.icvr.xyz/repository/npm/
    
  4. Clone the frontend repository and initialize the submodule:

    git clone <repo-url> ai-portal
    cd ai-portal
    git submodule update --init --recursive
    

    The xavier.grpc submodule is mandatory. If it is missing, dependency installation can fail because the prepare script calls scripts/generate-grpc.mjs.

Install Protocol Buffers Compiler

For this project, you need the Protocol Buffers compiler, protoc, because the gRPC generation script calls protoc to compile .proto files into generated TypeScript code.

winget install protobuf
protoc --version
brew install protobuf
protoc --version
sudo apt install protobuf-compiler
protoc --version

For more information, review the following article: Protocol Buffer Compiler Installation.

Install Dependencies and Generate gRPC Stubs

Run the following command:

pnpm install

During installation, the prepare script runs Husky setup and generates gRPC code from the proto contracts. The local setup note says a separate gRPC generation command is not needed after a normal install, because prepare already runs it. If backend developers update .proto files in the submodule and you pull those changes, regenerate the frontend stubs manually:

pnpm grpc:generate

gRPC uses Protocol Buffers as an interface definition language and message format, so the .proto files act as contracts between frontend-facing code and backend services.

Notify backend developers about contract changes

In Craftology, the frontend usually does not “own” these contracts. Backend developers commit changes to the submodule, and frontend developers inspect the contracts when they need to understand fields, service methods, or whether a new backend field is required. If a new field or method is needed, the frontend developer should coordinate with backend developers before changing the contract.

gRPC Contract FlowgRPC Contract Flowxavier.grpc submoduleai-portal-js.proto service contractsscripts/generate-grpc.mjsGenerated TS types / clientsBFF routessrc/app/api/*Backend developerFrontend developerBackend gRPC servicesupdates service contractpulls submodule updateinputgenerates frontend codeused by API routescalls through cluster
gRPC Contract FlowgRPC Contract Flowxavier.grpc submoduleai-portal-js.proto service contractsscripts/generate-grpc.mjsGenerated TS types / clientsBFF routessrc/app/api/*Backend developerFrontend developerBackend gRPC servicesupdates service contractpulls submodule updateinputgenerates frontend codeused by API routescalls through cluster

Pull Environment Variables from Vault

Create or refresh the local .env using the LDAP-based config command:

copy .env.template .env
pnpm config:ldap-dev
cp .env.template .env
pnpm config:ldap-dev
cp .env.template .env
pnpm config:ldap-dev

This command asks for LDAP credentials and writes environment variables from Vault into .env. The env file includes auth variables, data variables for PocketBase and optional S3 image URLs, service variables for PolicyVault gRPC, Temporal and SignalR, and observability variables such as Sentry and log level.

Be careful with local edits to .env: the local setup note says the Vault command overwrites .env, so any manual changes should be saved elsewhere before rerunning it.

Environment variables for other portals

You can use the same command to retrieve different envs, including config:ldap-dft, config:ldap-tst, config:ldap-stg, and config:ldap-prd; use those only when the task requires that environment and you have permission.

Install Telepresence

Because the frontend BFF talks to backend services in Kubernetes, local development needs cluster connectivity. The project uses Telepresence for that purpose. Telepresence connects a local workstation to a remote Kubernetes cluster so local code can reach cluster services as if they were available from the developer machine. For more information, review the following help topic: Getting Started with Telepresence.

Install and check Telepresence:

# Download the latest Windows installer from:
# https://github.com/telepresenceio/telepresence/releases

# Run the downloaded .exe installer.
# After installation, open a new PowerShell window and check:

telepresence version
brew install telepresenceio/telepresence/telepresence-oss
telepresence version
# AMD64
curl -fLO https://github.com/telepresenceio/telepresence/releases/latest/download/telepresence-linux-amd64.deb

# ARM64
# curl -fLO https://github.com/telepresenceio/telepresence/releases/latest/download/telepresence-linux-arm64.deb

sudo apt install ./telepresence-linux-*.deb
telepresence version

Connect Your Local Machine to Dev Cluster

Request a kubeconfig for the development cluster from the #helpdesk. In the helpdesk request, specify the telepresence command that you are requesting the configuration file for. The command sets the kubeconfig path:

set KUBECONFIG=C:\path\to\local\kubeconfig
export KUBECONFIG=/path/to/local/kubeconfig
export KUBECONFIG=/path/to/local/kubeconfig

The next command connects to Kubernetes cluster:

telepresence connect --manager-namespace telepresence --namespace backend --mapped-namespaces frontend,mlops
telepresence connect \
  --manager-namespace telepresence \
  --namespace backend \
  --mapped-namespaces frontend,mlops
telepresence connect \
  --manager-namespace telepresence \
  --namespace backend \
  --mapped-namespaces frontend,mlops

Use the following commands to verify connection and disconnect when finished:

REM Verify that Telepresence is connected
telepresence status

REM Verify that Kubernetes namespaces are reachable
kubectl get ns

REM Disconnect when finished
telepresence quit
# Verify that Telepresence is connected
telepresence status

# Verify that Kubernetes namespaces are reachable
kubectl get ns

# Disconnect when finished
telepresence quit

macOS first connection

On macOS, the first Telepresence connection may ask for sudo and permission in System Settings → Network.

# Verify that Telepresence is connected
telepresence status

# Verify that Kubernetes namespaces are reachable
kubectl get ns

# Disconnect when finished
telepresence quit

Keep VPN On

Keep the company VPN enabled while using Telepresence.

Run the Application

Start the dev server:

pnpm dev

This runs next dev --turbo, as specified in package.json.

Open the local app:

Open Local App

The server performs auth dependency checks at startup, including session secret, Redis, Keycloak, and PocketBase. If something is wrong, the process should exit with a clear error instead of failing later during login.

When the app is running, a newcomer should first confirm three things:

  • login works
  • Studio loads meaningful data
  • images render

If Studio data is empty or broken, check PocketBase and network/VPN access. If gRPC or PolicyVault calls fail, check that the submodule exists and regenerate stubs (pnpm grpc:generate). If images are broken, check S3/PocketBase-related environment variables and image host configuration.

Troubleshooting: PolicyVault gRPC timeout after pnpm dev

After running pnpm dev, the app performs startup checks for authentication, PocketBase, S3, PolicyVault, and other required services. If most checks pass but PolicyVault gRPC fails with a timeout, the frontend is usually configured correctly, but the local machine cannot reach the PolicyVault service inside the dev cluster.

The error usually looks like this:

PolicyVault gRPC        ✗ FAIL │ cannot connect to policyvault-dev-app-service...
Error: connection timeout after 5s

This can happen even when VPN is enabled. VPN may give access to the cluster network, but it does not always make Kubernetes service names, such as *.backend or *.svc.cluster.local, resolvable from the laptop.

First, check whether the service name resolves and whether the port is reachable.

Resolve-DnsName policyvault-dev-app-service.backend -ErrorAction SilentlyContinue
Test-NetConnection policyvault-dev-app-service.backend -Port 8080

If name resolution fails, or the TCP connection cannot be opened, use a local port-forward as a workaround.

Open a separate terminal and run:

kubectl port-forward -n backend svc/policyvault-dev-app-service 8080:8080

Keep this terminal window open while the frontend is running. The port-forward acts as a local bridge from localhost:8080 to the PolicyVault service in the dev cluster.

Then update the local .env file. Comment out the previous PolicyVault gRPC address settings and add:

POLICY_VAULT_GRPC_ADDR=localhost:8080
POLICY_VAULT_GRPC_TLS=false

Now run the dev server again:

pnpm dev

Note

This workaround is only for local development. It points the frontend to the locally forwarded PolicyVault port instead of relying on Kubernetes DNS from the laptop.

Tip

If port 8080 is already in use, choose another local port, for example 18080:8080, and set POLICY_VAULT_GRPC_ADDR=localhost:18080 in .env.

Understand Frontend Architecture

The repository layout is straightforward. src/app contains Next.js App Router pages and BFF routes under app/api. Most feature work happens in src/components. Data access patterns live in src/api-client and src/services. Aurora-specific UI lives in src/aurora, and Zustand stores live in the top-level stores directory.

The browser should not receive JWTs. Authentication goes through Keycloak; the server stores tokens in Redis and sends an HttpOnly session cookie to the browser. Then API routes under src/app/api attach the session credentials when calling upstream services. For user/session state in client components, use the project’s useAuth() hook.

TanStack Query is the preferred tool for server state. In general terms, TanStack Query is designed for fetching, caching, updating, and invalidating asynchronous server data in frontend applications. In Craftology terms, that means a new feature should usually expose data through a hook in src/api-client, call a lower-level service if needed, and let React components consume the hook instead of scattering ad hoc fetches through the UI. Pay attention to query keys, cache reuse, and invalidation as part of the team’s data-handling approach.

Craftology Frontend Runtime ArchitectureCraftology Frontend Runtime ArchitectureBrowserNext.js App RouterReact Client ComponentsTanStack Query Hookssrc/api-clientZustand Storesstores/Pages / Layoutssrc/appBFF API Routessrc/app/apiAuth SessionHttpOnly cookieUserRedissession storeKeycloakOIDCPocketBasePolicyVault gRPCTemporal / other servicesS3 / private object storage
Craftology Frontend Runtime ArchitectureCraftology Frontend Runtime ArchitectureBrowserNext.js App RouterReact Client ComponentsTanStack Query Hookssrc/api-clientZustand Storesstores/Pages / Layoutssrc/appBFF API Routessrc/app/apiAuth SessionHttpOnly cookieUserRedissession storeKeycloakOIDCPocketBasePolicyVault gRPCTemporal / other servicesS3 / private object storage

Learn by Following Real Flow

Craftology Local Runtime FlowCraftology Local Runtime FlowLocal machineDev cluster accessDev backend servicesNext.js apppnpm devReact UIStudio pagesBFF API routessrc/app/api/*TelepresencePocketBaseapp dataPolicyVault gRPCroles / access / policiesML / MLOps servicesAI generation flowsPrivate file storageimages / assetsDeveloperBrowserThe browser talks to local Next.js API routes,not directly to private backend services.Telepresence makes dev-cluster servicesreachable from the local machine.opens localhost:3000updates Studio UIrenders app routescalls local API routesnormalized responsereaches cluster servicesfile or signed/proxied URLrecords / updatesaccess decisionsgenerated data updates
Craftology Local Runtime FlowCraftology Local Runtime FlowLocal machineDev cluster accessDev backend servicesNext.js apppnpm devReact UIStudio pagesBFF API routessrc/app/api/*TelepresencePocketBaseapp dataPolicyVault gRPCroles / access / policiesML / MLOps servicesAI generation flowsPrivate file storageimages / assetsDeveloperBrowserThe browser talks to local Next.js API routes,not directly to private backend services.Telepresence makes dev-cluster servicesreachable from the local machine.opens localhost:3000updates Studio UIrenders app routescalls local API routesnormalized responsereaches cluster servicesfile or signed/proxied URLrecords / updatesaccess decisionsgenerated data updates

A common pattern is as follows: the frontend sends a request to a BFF route, backend or ML services update PocketBase, and the frontend subscribes to updated collections so data appears in near real time. Another important pattern is image access: the frontend may request one URL, while the BFF/proxy layer rewrites or proxies access to private object storage.

When reading a flow, avoid starting with global architecture. Start from the visible screen, inspect the component, find the hook or service it uses, and then trace the BFF route if there is one. This mirrors the onboarding recommendation: use browser devtools and React DevTools to identify the component that renders the thing you are looking at, then find the matching code and verify you are editing the actual component used on the page.

Your First Safe Task

A good first task is intentionally small: find a page that renders generated images, inspect the image component, and verify that images keep the correct aspect ratio at several viewport widths. This task proves the developer can run the app, inspect the UI, locate the relevant component, make a safe frontend change, and see the result locally without touching risky backend behavior.

Start in the browser. Open a page with images, then use the element inspector to check the rendered image element and its styles. Open React DevTools and identify the component name. Search the repository for that component, then confirm the code path by making a harmless local-only visual change. Once you know you are in the right place, check responsive behavior. If the aspect ratio breaks, fix the styling in the smallest appropriate component rather than adding a broad global override.

The expected result is not make a random CSS change. The expected result is a clean, reviewable change with a short explanation: which screen was checked, what broke, which component owns the behavior, and how the change was validated. If everything already works, the developer can still document the validation and ask for the next task.

Coding Conventions and Habits

The project does not appear to have a long custom style guide beyond normal TypeScript/React practices and the conventions enforced by tooling. However, you should emphasize reuse: prefer existing components, hooks, services, and patterns; avoid writing one-off abstractions that make the repository wider and harder to navigate.

New source files need the ICVR copyright header. There exists a script that checks for missing headers and generates them where needed, and package.json confirms add-headers and add-headers:check scripts.

Git and Review Flow

The team workflow is simple: branch from main, work in a feature branch, open a merge request, and go through review. There are no special Git tricks; the key rule is to use the required branch or ticket prefix because validation depends on it.

For releases, the team uses prepared pipelines and release branches. Changes intended for release are pulled into a release branch, the pipeline builds the project, and then a deploy button allows an authorized person to deploy to the selected environment. Ordinary developers usually do not have deployment rights; deployment is typically handled by a designated person.

The environments are DEV, DFT/draft, TST/test, STG/stage, and PROD. DEV is for regular development, DFT is for experimental work that might disrupt DEV, TST is for QA team, STG is pre-production, and PROD is production.

What to ask when stuck

When the app does not start, read the startup error first; dependency checks should point to missing or invalid session, Redis, Keycloak, or PocketBase configuration. When generated types are missing or gRPC calls fail, check the submodule and rerun pnpm grpc:generate. When environment variables cannot be pulled, verify LDAP/Vault access. When cluster services are unreachable, verify VPN, kubeconfig, and Telepresence status.

For product questions, ask the lead or product owner. For backend contract questions, inspect the relevant .proto file first, then talk to backend developers if the frontend needs a new field or changed contract. For data/schema changes in PocketBase, coordinate with the lead and announce agreed changes in the shared thread so environments stay aligned.

Done means the developer is ready for real work

Onboarding is complete when the developer can run the app locally, connect to the dev cluster, log in, inspect a real screen, find the responsible component, make a small safe change, validate it in the browser, and open a merge request. That is the practical threshold: once the developer has installed the environment, cloned the repo, made a small change, and seen it working locally, they are ready to take a normal task.