Skip to content

Adding a Service

Adding a new service to AKKO involves touching several files to ensure the service is routed, secured, monitored, and visible in the cockpit portal. This page provides the full checklist and a concrete example.


Checklist

Step File Purpose
1 helm/akko/charts/<sub-chart>/ Helm sub-chart (templates, values, helpers)
2 helm/examples/realm-akko-k3d.json OAuth2 client (if the service needs SSO)
3 scripts/generate-secrets.sh Add any new secrets to .env generation
4 branding/cockpit-react/src/platform/catalog.ts Register the service as a capability in a layer
5 branding/cockpit-react/src/features/<x>/ Add a feature page (only if the service is rendered inside the cockpit)
6 branding/cockpit-react/src/app/App.tsx Add the route (only for an integrated feature page)
7 scripts/start.sh Echo the service URL at startup
8 Documentation Update architecture docs, README, etc.

Each step is detailed below, followed by a complete worked example.


Step 1 -- Helm Sub-Chart

Create a Helm sub-chart in helm/akko/charts/<service-name>/ and add the dependency to helm/akko/Chart.yaml. Every AKKO service follows a consistent pattern:

  • Pinned image tag (never latest)
  • Container name prefixed with akko-
  • Traefik IngressRoute for HTTPS routing
  • Healthcheck with liveness and readiness probes
  • Resource limits appropriate for the service
  • Security context (runAsNonRoot, drop: [ALL])

Traefik Labels Pattern

All services exposed through Traefik use these four labels:

labels:
  traefik.enable: "true"
  traefik.http.routers.<name>.rule: "Host(`<subdomain>.{{ .Values.global.domain }}`)"
  traefik.http.routers.<name>.entrypoints: "websecure"
  traefik.http.routers.<name>.tls: "true"

If the service listens on a non-standard port (anything other than 80), add:

  traefik.http.services.<name>.loadbalancer.server.port: "<port>"

To protect the service behind Keycloak SSO via oauth2-proxy, add the middleware:

  traefik.http.routers.<name>.middlewares: "oauth2-auth-chain@file"
  # Plus the oauth2 callback router:
  traefik.http.routers.<name>-oauth2.rule: "Host(`<subdomain>.{{ .Values.global.domain }}`) && PathPrefix(`/oauth2/`)"
  traefik.http.routers.<name>-oauth2.entrypoints: "websecure"
  traefik.http.routers.<name>-oauth2.tls: "true"
  traefik.http.routers.<name>-oauth2.service: "oauth2-proxy"

Healthcheck Pattern

livenessProbe:
  httpGet:
    path: /<health-path>
    port: <port>
  initialDelaySeconds: 30
  periodSeconds: 30
  timeoutSeconds: 10
  failureThreshold: 3
readinessProbe:
  httpGet:
    path: /<health-path>
    port: <port>
  initialDelaySeconds: 10
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

Healthcheck tips

  • Use httpGet probes for HTTP health endpoints.
  • Use exec probes with the service's native healthcheck command when available (e.g., ["traefik", "healthcheck"] or ["mc", "ready", "local"]).
  • Some minimal images (like Ollama's) lack curl and wget. Use tcpSocket probes as a fallback.

Step 2 -- Keycloak OAuth Client

If the service supports OpenID Connect, add a client to helm/examples/realm-akko-k3d.json in the clients array. The client ID should match the service name. Set publicClient: false for server-side (confidential) flows.


Step 3 -- Secret Generation

If the service needs credentials, add them to scripts/generate-secrets.sh inside the heredoc that writes .env:

# --- MyService ---
MYSERVICE_ADMIN_PASSWORD=$(gen_password)
KC_CLIENT_SECRET_MYSERVICE=$(gen_hex)

After editing, delete .env and re-run to pick up the new variables:

rm .env && ./scripts/generate-secrets.sh

Step 4 -- Cockpit Capability

The cockpit is a React SPA (branding/cockpit-react/). Register the service as a capability inside its layer in src/platform/catalog.ts. Each capability carries the roles allowed to see it and a kind:

  • kind: 'external' -- the service has its own dedicated UI, opened in a new tab (most services: Trino, Superset, JupyterHub, ...)
  • kind: 'integrated' -- the service is rendered as a page inside the cockpit (a React feature), reached via an internal to: '/x/<slug>' route
src/platform/catalog.ts
{ id: 'governance', fr: 'Gouvernance', en: 'Governance', icon: 'shield', color: '--cl-gov', caps: [
  // external tool: opens its own UI in a new tab
  { id: 'myservice', tool: 'myservice', fr: 'Mon service', en: 'My service',
    dfr: 'Description courte', den: 'Short description',
    vendor: 'myservice', icon: 'database', roles: ENG, kind: 'external', home: true },
] },

The roles field is the visibility gate: canSeeCap(cap, role, toolAccess) in src/session/SessionProvider reads the same tool-access matrix that drives the OPA gate (ADR-075), so a card only appears for a user entitled to the tool. There is no health-proxy to configure: per-tile health comes from the cockpit backend.


Step 5 -- Feature Page (integrated services only)

Skip this step for an external service. If the service is rendered inside the cockpit, add a feature under src/features/<x>/:

src/features/myservice/
├── MyServicePage.tsx   # the page component
└── i18n.ts             # FR/EN dictionary (never hard-code strings in JSX)

The page reads its strings from the i18n.ts dictionary via useLang() / a t() helper, matching the existing features (RAG, NORA, ...).


Step 6 -- Route (integrated services only)

Register the page's route in src/app/App.tsx, before the x/:slug catch-all:

src/app/App.tsx
{ path: 'x/myservice', element: <MyServicePage /> },

The cockpit uses a HashRouter (ADR-074), so no nginx rewrite rule is required to serve the static island.


Step 7 -- Startup URL

Add the service URL to the startup banner in scripts/start.sh:

echo "  MyService:     https://myservice.$AKKO_DOMAIN"

Step 8 -- Documentation

Update architecture documentation and any relevant guides to reflect the new service.


Worked Example: Adding "DataHub"

Here is a complete, concrete example of adding a hypothetical DataHub metadata catalog service to AKKO.

1. Helm Sub-Chart

Create helm/akko/charts/akko-datahub/ with the standard sub-chart structure:

helm/akko/charts/akko-datahub/
├── Chart.yaml
├── values.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── _helpers.tpl

Add the dependency in helm/akko/Chart.yaml:

- name: akko-datahub
  version: 0.1.0
  condition: akko-datahub.enabled

Example values.yaml:

enabled: true
image:
  repository: acryldata/datahub-gms
  tag: "v0.13.0"
  pullPolicy: IfNotPresent
resources:
  requests:
    memory: 512Mi
    cpu: 250m
  limits:
    memory: 1Gi
    cpu: 500m

2. helm/examples/realm-akko-k3d.json

Add a confidential client in the clients array:

{
  "clientId": "datahub",
  "enabled": true,
  "publicClient": false,
  "secret": "${KC_CLIENT_SECRET_DATAHUB}",
  "redirectUris": ["https://datahub.akko.local/*"],
  "webOrigins": ["https://datahub.akko.local"],
  "protocol": "openid-connect",
  "standardFlowEnabled": true,
  "directAccessGrantsEnabled": false
}

3. scripts/generate-secrets.sh

Add to the heredoc:

# --- DataHub ---
DATAHUB_ADMIN_PASSWORD=$(gen_password)
KC_CLIENT_SECRET_DATAHUB=$(gen_hex)

4. branding/cockpit-react/src/platform/catalog.ts

DataHub is a metadata catalog with its own UI, so it is an external capability in the Governance layer:

{ id: 'datahub', tool: 'datahub', fr: 'Catalogue de métadonnées',
  en: 'Metadata catalog', dfr: 'Découverte & lignage',
  den: 'Discovery & lineage', vendor: 'datahub', icon: 'database',
  roles: ENG, kind: 'external', home: true },

5 & 6. Feature page and route (not needed here)

DataHub opens in its own tab (kind: 'external'), so no React feature page and no App.tsx route are required. These two steps only apply to an integrated service rendered inside the cockpit.

7. scripts/start.sh

echo "  DataHub:       https://datahub.$AKKO_DOMAIN"

Verification

After completing all steps:

# Regenerate secrets
rm .env && ./scripts/generate-secrets.sh

# Deploy with Helm
helm upgrade akko helm/akko/ -n akko -f helm/examples/values-dev.yaml \
  --set-file akko-keycloak.realm.data=helm/examples/realm-akko-k3d.json

# Verify DataHub is healthy
kubectl get pods -n akko | grep datahub
kubectl logs -f deploy/akko-datahub -n akko

# Open the cockpit and confirm the DataHub tile appears (for an entitled role)
open https://demo.akko.local

# Open the service directly
open https://datahub.akko.local