Skip to main content

Shell Frontend Deployment Guide

The Shell is a Nuxt 4 server-side rendered frontend that provides the platform UI, authentication flows, and service proxying.

Overview

The Shell provides:

  • Server-side rendered Vue.js application
  • Authentication UI (login, logout, consent flows)
  • App container for microfrontends
  • GraphQL proxy to AppServer
  • Legacy system proxy (Core5 integration)
┌─────────────────────────────────────────────────────────────┐
│ Shell Frontend │
│ (Nuxt 4 / Node.js) │
├─────────────────────────────────────────────────────────────┤
│ Pages & Components │ Server API Routes │
│ ├── /login │ ├── /api/host/me │
│ ├── /logout │ ├── /api/host/logout │
│ ├── /consent │ ├── /api/auth/hydra/* │
│ ├── /apps/* │ ├── /graphql (proxy) │
│ └── /settings │ └── /c5/* (legacy proxy) │
└─────────────────────────────────────────────────────────────┘
│ │
└──────────────┬───────────────┘

┌───────────────────┼───────────────────┐
▼ ▼ ▼
Kratos AppServer Legacy (C5)
(Identity) (GraphQL) (Proxy)

Tech Stack

TechnologyVersionPurpose
Nuxt4.xMeta-framework
Vue3.5.xUI framework
Tailwind CSS4.xStyling
Node.js20+Runtime
Nitro2.xServer engine

Docker Build

Image Properties

PropertyValue
Build imagenode:20-alpine
Runtime imagenode:20-alpine
Image size~230MB
Usernuxt (uid 1001)
Port3000
Entry pointnode .output/server/index.mjs

Build Commands

With SSH agent (recommended for local development):

docker build --ssh default \
--build-arg NPM_TOKEN="$(grep _authToken ~/.npmrc | head -1 | cut -d= -f2 | tr -d '\"')" \
-f docker/shell/Dockerfile \
-t shell:latest .

With SSH keys as build args (for CI/CD):

docker build \
--build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" \
--build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" \
--build-arg NPM_TOKEN="${NPM_TOKEN}" \
-f docker/shell/Dockerfile \
-t shell:latest .

Build Arguments

ArgumentDefaultDescription
NPM_TOKEN-Required. Auth token for private npm registry
NPM_REGISTRYhttps://npm.eacore6.de/Private npm registry URL
ssh_prv_key-SSH private key for Bitbucket access
ssh_pub_key-SSH public key for Bitbucket access

Run Container

docker run -p 3000:3000 --env-file docker/.env shell:latest

Environment Variables

Server-side vs Client-side Variables

The Shell has two types of environment variables:

Server-side variables (e.g., KRATOS_PUBLIC_URL, DATABASE_URL) are read directly from process.env at container runtime. Just set them in your environment.

Client-side variables (browser-accessible, prefixed with NUXT_PUBLIC_) are injected into the HTML by Nuxt at request time. Use the NUXT_PUBLIC_ prefix for these.

In docker-compose, map your standard env vars to the NUXT_PUBLIC_ format:

services:
shell:
environment:
# Server-side (read directly)
KRATOS_PUBLIC_URL: ${KRATOS_PUBLIC_URL}
DATABASE_URL: ${DATABASE_URL}
# Client-side (needs NUXT_PUBLIC_ prefix)
NUXT_PUBLIC_C5_BASE_URL: ${C5_BASE_URL}
NUXT_PUBLIC_APP_SERVER_HTTP_URL: ${APPSERVER_HTTP_URL}

Authentication Services

VariableDefaultDescription
KRATOS_PUBLIC_URL-Required. Kratos public API endpoint
KRATOS_ADMIN_URL-Required. Kratos admin API endpoint
KRATOS_WEBHOOK_SECRET-Webhook secret (32+ chars), must match Kratos config
HYDRA_PUBLIC_URL-Required. Hydra public API endpoint
HYDRA_ADMIN_URL-Required. Hydra admin API endpoint

Authorization

VariableDefaultDescription
OPENFGA_API_URL-Required. OpenFGA API endpoint
OPENFGA_STORE_ID-Required. OpenFGA store ID (must match AppServer)

Database

VariableDefaultDescription
DATABASE_URL-PostgreSQL connection string for shell database

Format: postgresql://user:password@host:5432/database

AppServer Backend

VariableDefaultDescription
NUXT_PUBLIC_APPSERVER_GRAPHQL_URL/graphqlGraphQL endpoint (browser-accessible)
NUXT_PUBLIC_APPSERVER_HTTP_URL-AppServer HTTP URL for server-side requests

Legacy Proxy (Optional)

VariableDefaultDescription
C5_BASE_URL-Core5 legacy backend URL
C5_SESSION_COOKIE_NAMElegacy_sessionLegacy session cookie name

Telemetry (Optional)

VariableDefaultDescription
TELEMETRY_ENABLEDfalseEnable OpenTelemetry
TELEMETRY_SERVICE_NAMEcore6-shellService name in traces
OTEL_EXPORTER_OTLP_ENDPOINT-OTLP HTTP endpoint
LOG_LEVELinfoLog level: debug, info, warn, error

Nginx Reverse Proxy

For production with SSL termination:

upstream shell {
server shell:3000;
}

upstream appserver {
server appserver:8080;
}

server {
listen 443 ssl http2;
server_name app.example.com;

ssl_certificate /etc/ssl/certs/app.crt;
ssl_certificate_key /etc/ssl/private/app.key;

# Shell frontend
location / {
proxy_pass http://shell;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# AppServer API (direct access)
location /api/v1/ {
proxy_pass http://appserver;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}

# WebSocket support for GraphQL subscriptions
location /graphql {
proxy_pass http://shell;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}

Local Development

Setup

cd web/v2/packages/shell

# Install dependencies
pnpm install

# Copy environment template
cp .env.example .env

# Edit .env with your values
# - Set OPENFGA_STORE_ID from: curl http://localhost:8090/stores | jq -r '.stores[0].id'
# - Update DATABASE_URL password from docker/.env

# Start development server
pnpm dev

Production Build (without Docker)

# Build for production
pnpm build

# Output directory: .output/
# Entry point: .output/server/index.mjs

# Start production server
node .output/server/index.mjs

Production Checklist

Required Configuration

  • Build with pnpm build or Docker
  • Set NODE_ENV=production
  • Configure all auth service URLs:
    • KRATOS_PUBLIC_URL
    • KRATOS_ADMIN_URL
    • HYDRA_PUBLIC_URL
    • HYDRA_ADMIN_URL
  • Set KRATOS_WEBHOOK_SECRET (32+ chars, must match Kratos config)
  • Set OPENFGA_API_URL and OPENFGA_STORE_ID (must match AppServer)
  • Configure DATABASE_URL with production credentials

Optional Configuration

  • Configure C5_BASE_URL for legacy proxy (if needed)
  • Enable telemetry (TELEMETRY_ENABLED=true)
  • Set up SSL termination (nginx/load balancer)
  • Configure health checks

Security

  • Run container as non-root user (default: nuxt)
  • Use secrets management for sensitive values
  • Enable HTTPS in production
  • Set secure cookie policies

Troubleshooting

Login Not Working

Symptom: Login redirects fail or loop

  1. Check Kratos URL is accessible:

    curl http://kratos:4433/health/ready
  2. Verify webhook secret matches:

    # Shell .env
    KRATOS_WEBHOOK_SECRET=...
    # docker/.env (must match!)
    KRATOS_WEBHOOK_SECRET=...
  3. Check Kratos configuration has correct browser URLs

GraphQL Proxy Errors

Symptom: GraphQL requests fail

  1. Verify AppServer is running:

    curl http://appserver:8080/health
  2. Check network connectivity from shell container:

    docker exec shell wget -qO- http://appserver:8080/health

Permission Denied Errors

Symptom: Users can't access certain features

  1. Verify OPENFGA_STORE_ID matches AppServer config
  2. Check user has correct roles in OpenFGA
  3. Review OpenFGA authorization model

Database Connection Errors

Symptom: Shell fails to start or user lookup fails

  1. Verify DATABASE_URL format:
    postgresql://user:password@host:5432/database
  2. Check PostgreSQL is accessible
  3. Ensure database exists (created by migrations)

Container Won't Start

Symptom: Container exits immediately

  1. Check logs:

    docker logs shell
  2. Verify all required environment variables are set

  3. Test locally first:

    node .output/server/index.mjs

Monitoring

Health Check

curl http://localhost:3000/

Logs

# Docker
docker logs shell -f

# Kubernetes
kubectl logs -f deployment/shell

Telemetry

When TELEMETRY_ENABLED=true:

  • Traces exported to OpenTelemetry Collector
  • Service name: core6-shell
  • Logs include correlation IDs