18 Commits

Author SHA1 Message Date
077be9fbf3 cmd
All checks were successful
AI Code Review / ai-review (pull_request) Successful in 4s
2026-04-20 13:39:44 +02:00
16da2fa6b3 vars
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 3s
2026-04-20 13:39:02 +02:00
9ab283f1e5 workflow fix
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 9s
2026-04-20 13:37:40 +02:00
e06b270e67 pip
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 8s
2026-04-20 13:11:30 +02:00
89d2952d7a flag
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 8s
2026-04-20 13:09:06 +02:00
3d6eadf128 workflow fix
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 9s
2026-04-20 13:07:50 +02:00
260b45637e AI-review
Some checks failed
AI Code Review / ai-review (pull_request) Failing after 8s
2026-04-20 13:02:48 +02:00
723072bd1e cleanup 2026-04-19 13:47:29 +02:00
046b78446b add opencost 2026-04-19 13:41:44 +02:00
56a1b49d10 missing manifest 2026-04-19 13:39:26 +02:00
d557eb1865 revert 2026-04-19 13:28:40 +02:00
a51ed84124 Merge branch 'main' of https://git.forteapps.net/Forte/launchpad 2026-04-19 13:28:03 +02:00
73e253a579 traefik 2026-04-19 13:27:59 +02:00
d7c1341eab don't sync users with cron job 2026-04-19 11:43:47 +02:00
eed53006c1 docs 2026-04-18 23:12:18 +02:00
395ca70c2a prod values 2026-04-18 23:02:02 +02:00
ea04ec20c9 remove docs wf 2026-04-18 20:54:48 +02:00
03a0d7c9ae feature/multicluster
Some checks failed
Deploy Gitea Pages / build-and-deploy (push) Failing after 5s
Co-authored-by: Danijel Simeunovic <danijel.simeunovic@trumf.no>
Reviewed-on: #4
Reviewed-by: gitea_admin <admin@forteapps.net>
2026-04-18 18:14:00 +00:00
75 changed files with 824 additions and 452 deletions

View File

@@ -0,0 +1,41 @@
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
ai-review:
runs-on: ubuntu-latest
env:
AI_REVIEW_CONFIG_FILE_YAML: ./shared-prompts/iac/.ai-review.yaml
# VCS configuration
VCS__PROVIDER: GITEA
VCS__PIPELINE__OWNER: ${{ github.repository_owner }}
VCS__PIPELINE__REPO: ${{ github.event.repository.name }}
VCS__PIPELINE__PULL_NUMBER: ${{ github.event.pull_request.number }}
VCS__HTTP_CLIENT__API_URL: https://git.forteapps.net/api/v1
VCS__HTTP_CLIENT__API_TOKEN: ${{ secrets.AI_REVIEW_TOKEN }}
# LLM configuration
LLM__PROVIDER: CLAUDE
LLM__META__MODEL: claude-sonnet-4-20250514
LLM__HTTP_CLIENT__API_URL: https://api.anthropic.com
LLM__HTTP_CLIENT__API_TOKEN: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Run inline review
uses: docker://nikitafilonov/ai-review:latest
with:
args: ai-review run-inline
- name: Run summary review
uses: docker://nikitafilonov/ai-review:latest
with:
args: ai-review run-summary

View File

@@ -1,34 +0,0 @@
name: Deploy Gitea Pages
on:
push:
branches: [ main ]
paths:
- 'docs/**'
- 'mkdocs.yml'
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
apt-get update -qq
apt-get install -y -qq python3-pip
pip3 install --break-system-packages mkdocs mkdocs-material
- run: mkdocs build
- name: Deploy to Gitea Pages
run: |
cd site
git init
git config user.name "gitea-actions"
git config user.email "actions@forteapps.net"
git add .
git commit -m "Deploy docs"
git push --force "https://x-token:${{ secrets.GITEA_TOKEN }}@git.forteapps.net/Forte/launchpad.git" HEAD:gitea-pages

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "shared-prompts"]
path = shared-prompts
url = https://git.forteapps.net/Forte/ai-review-prompts.git

View File

@@ -83,20 +83,26 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
├── bootstrap.sh # Cluster initialization script
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
├── infra/ # Infrastructure ArgoCD Applications
│ ├── enterprise-apps.yaml # Manages all apps in apps/ folder
│ ├── traefik-application.yaml
│ ├── cert-manager-application.yaml
│ ├── kyverno.yaml
│ ├── prometheus.yaml
│ ├── grafana.yaml
│ ├── loki.yaml
│ ├── tempo.yaml
│ ├── fluent-bit.yaml
│ ├── trivy.yaml
├── sealedsecrets.yaml
│ ├── renovate.yaml
├── infra/ # Infrastructure ArgoCD Applications (Kustomize multi-cluster)
│ ├── base/ # Base ArgoCD Application manifests (EU defaults)
│ ├── kustomization.yaml
│ ├── traefik-application.yaml
│ ├── keycloak.yaml
│ ├── grafana.yaml
│ ├── gitea.yaml
│ ├── gitea-actions.yaml
│ ├── tempo.yaml
│ ├── renovate.yaml
│ ├── ... # All other Application manifests
│ └── secrets.yaml
│ ├── overlays/ # Per-cluster overrides
│ │ ├── upc-dev/ # UpCloud Dev cluster (uses base as-is)
│ │ └── upc-prod/ # UpCloud Prod cluster (patches value paths)
│ ├── dashboards/ # Grafana dashboard ConfigMaps
│ └── values/ # Helm value overrides
│ ├── base/ # Shared values (all clusters)
│ ├── upc-dev/ # UpCloud Dev-specific values
│ └── upc-prod/ # UpCloud Prod-specific values
├── apps/ # Business Applications
│ ├── mcp10x.yaml
@@ -140,12 +146,12 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
|------------|---------|-----------|-----------|
| **[launchpad](https://git.forteapps.net/Forte/launchpad)** (this repo) | ArgoCD Applications, cluster resources | Platform / DevOps engineers | ✅ Often |
| **[forte-helm](https://git.forteapps.net/Forte/forte-helm)** | Generic Helm chart templates | Platform engineers | ❌ Rarely |
| **[helm-values](ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git)** | App-specific configuration & versions | Developers / CI pipelines | ✅ Sometimes |
| **[helm-prod-values](ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git)** | App-specific configuration & versions | Developers / CI pipelines | ✅ Sometimes |
### GitOps Workflow
```
Developer commits code → CI/CD builds image → Updates helm-values → ArgoCD syncs → Deployed to cluster
Developer commits code → CI/CD builds image → Updates helm-prod-values → ArgoCD syncs → Deployed to cluster
```
**Learn more**: [GitOps Architecture - GitOps Workflow](docs/GITOPS-ARCHITECTURE.md#gitops-workflow)
@@ -160,7 +166,7 @@ Developer commits code → CI/CD builds image → Updates helm-values → ArgoCD
**Quick version**:
1. Create `apps/myapp.yaml` (ArgoCD Application manifest)
2. Create `helm-values/myapp/values.yaml` (configuration)
2. Create `helm-prod-values/myapp/values.yaml` (configuration)
3. Create sealed secrets if needed
4. Commit and push - ArgoCD auto-syncs!
@@ -169,8 +175,8 @@ Developer commits code → CI/CD builds image → Updates helm-values → ArgoCD
**See detailed guide**: [Developer Guide - Updating an Existing Application](docs/DEVELOPER-GUIDE.md#updating-an-existing-application)
**Quick version**:
- **Update code**: Push to app repo → CI/CD updates image tag in helm-values
- **Update config**: Edit `helm-values/myapp/values.yaml` → commit → push
- **Update code**: Push to app repo → CI/CD updates image tag in helm-prod-values
- **Update config**: Edit `helm-prod-values/myapp/values.yaml` → commit → push
### Manage Secrets
@@ -198,7 +204,7 @@ git push
**Quick version**:
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
# Token-based auth (simple)
auth:
@@ -355,12 +361,12 @@ kubectl patch application myapp -n argocd \
## 📖 Key Concepts
### App-of-Apps Pattern
`_app-of-apps.yaml` is the root Application that manages all other Applications in `infra/`. Each YAML in `infra/` becomes a child Application managed by ArgoCD.
`_app-of-apps.yaml` is the root Application that manages all other Applications in `infra/`. Kustomize overlays in `infra/overlays/{upc-dev,upc-prod}/` render the base Applications with per-cluster patches (e.g., swapping value file paths from `upc-dev` to `upc-prod`).
### Multi-Source Pattern
Applications reference both:
1. **Helm charts** from `forte-helm` (templates)
2. **Values** from `helm-values` (configuration)
2. **Values** from `helm-prod-values` (configuration)
This separates reusable templates from environment-specific config.
@@ -429,7 +435,7 @@ Applications deploy in order using `argocd.argoproj.io/sync-wave`:
### Adding a New Application
1. Read [Developer Guide - Deploying Your First Application](docs/DEVELOPER-GUIDE.md#deploying-your-first-application)
2. Create ArgoCD Application manifest in `apps/`
3. Create Helm values in `helm-values/`
3. Create Helm values in `helm-prod-values/`
4. Create sealed secrets if needed
5. Commit and push - ArgoCD handles the rest!
@@ -454,14 +460,14 @@ Documentation lives in `docs/`. To update:
### Current Environment
- **Provider**: UpCloud Managed Kubernetes
- **Environment**: Production (internal use only)
- **Cluster**: Single cluster
- **Clusters**: Multi-cluster (upc-dev, upc-prod) via Kustomize overlays
- **Auth**: Disabled for ArgoCD (internal access)
- **Backup**: None (cluster rebuildable via GitOps)
### Known Limitations
- No automated backups (yet)
- Secret rotation not automated
- Single cluster (no multi-cluster setup)
- Multi-cluster limited to upc-dev and upc-prod environments
- DNS management is manual
**Future improvements**: See [Operations Runbook - Disaster Recovery](docs/OPERATIONS-RUNBOOK.md#disaster-recovery)
@@ -479,8 +485,8 @@ Documentation lives in `docs/`. To update:
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
### Related Repositories
- [forte-helm](https://github.com/fortedigital/forte-helm) - Helm chart templates
- [helm-values](git@github.com:fortedigital/helm-values.git) - Application values
- [forte-helm](https://git.forteapps.net/Forte/forte-helm) - Helm chart templates
- [helm-prod-values](git@github.com:fortedigital/helm-prod-values.git) - Application values
---

View File

@@ -20,7 +20,7 @@ spec:
source:
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
path: infra
path: infra/overlays/upc-dev
destination:
server: https://kubernetes.default.svc
namespace: default

View File

@@ -0,0 +1,32 @@
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
annotations:
argocd.argoproj.io/sync-wave: "-1"
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: infrastructure-apps
namespace: argocd
labels:
app.kubernetes.io/name: infrastructure-apps
app.kubernetes.io/part-of: platform
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: git@github.com:fortedigital/sturdy-adventure.git
targetRevision: HEAD
path: infra/overlays/upc-prod
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View File

@@ -27,29 +27,19 @@ metadata:
spec:
project: default
source:
repoURL: ghcr.io/vfarcic/dot-ai-stack/charts
sources:
- repoURL: ghcr.io/vfarcic/dot-ai-stack/charts
chart: dot-ai-stack
targetRevision: "0.56.0"
helm:
releaseName: dot-ai-stack
values: |
dot-ai:
ingress:
enabled: true
className: traefik
host: kubemcp.forteapps.net
webUI:
baseUrl: http://kubemcpui.forteapps.net
dot-ai-ui:
uiAuth:
secretRef:
name: dot-ai-secrets
ingress:
enabled: true
className: traefik
host: kubemcpui.forteapps.net
valueFiles:
- $values/infra/values/base/dot-ai-stack-values.yaml
- $values/infra/values/upc-dev/dot-ai-stack-values.yaml
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc

View File

@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- dot-ai-stack.yaml
- mcp10x.yaml
- musicman.yaml
- argo-mcp.yaml

View File

@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
# No patches needed — base already has "upc-dev" paths
# upc-dev is the default/base cluster

View File

@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
# dot-ai-stack: swap upc-dev → upc-prod
- target:
kind: Application
name: dot-ai-stack
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/dot-ai-stack-values.yaml

View File

@@ -2,7 +2,14 @@
# in case of $'\r': command not found error, run command below first
# sed -i 's/\r$//' ./bootstrap.sh
echo "running $0..."
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (upc-dev|upc-prod)}"
echo "running $0 for cluster: ${CLUSTER}..."
# Source cluster config
eval $(yq -r 'to_entries[] | "export \(.key)=\"\(.value)\""' "clusters/${CLUSTER}.yaml")
echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..."
############################################################
# Bootstrap #
@@ -10,17 +17,17 @@ echo "running $0..."
Bootstrap()
{
ArgoCd
# Github
# Gitea
}
############################################################
# Github #
# Gitea #
############################################################
Github()
Gitea()
{
echo "Installing secret..."
kubectl apply -f private/github.yaml
kubectl apply -f private/gitea-repo-main.yaml
kubectl apply -f private/main.key
}
@@ -31,15 +38,15 @@ ArgoCd()
{
# install argocd
echo "Installing ArgoCD..."
CLUSTER_NAME="${CLUSTER_NAME:-dev-fd-no-svg1}"
helm upgrade --install argocd argo-cd \
--repo https://argoproj.github.io/argo-helm \
--namespace argocd --create-namespace \
--values infra/values/argocd-values.yaml \
--set notifications.context.clusterName="$CLUSTER_NAME" \
--values infra/values/base/argocd-values.yaml \
--values "infra/values/${CLUSTER}/argocd-values.yaml" \
--set notifications.context.clusterName="${clusterName}" \
--timeout 60s --atomic
kubectl apply -f _app-of-apps.yaml -n argocd
kubectl apply -f "_app-of-apps-${CLUSTER}.yaml" -n argocd
}
Bootstrap
# Bootstrap

10
clusters/upc-dev.yaml Normal file
View File

@@ -0,0 +1,10 @@
clusterName: dev-fd-no-svg1
domain: forteapps.net
argocdDomain: argocd.127.0.0.1.nip.io
grafanaDomain: grafana.forteapps.net
keycloakDomain: id.forteapps.net
dotaiDomain: kubemcp.forteapps.net
dotaiUiDomain: kubemcpui.forteapps.net
letsencryptEmail: danijels@gmail.com
trustedIPs: "172.16.1.0/24"
cloudProvider: upcloud

10
clusters/upc-prod.yaml Normal file
View File

@@ -0,0 +1,10 @@
clusterName: prod-fd-no-svg1
domain: fortedigital.com
argocdDomain: argocd.127.0.0.1.nip.io
grafanaDomain: grafana.fortedigital.com
keycloakDomain: id.fortedigital.com
dotaiDomain: kubemcp.fortedigital.com
dotaiUiDomain: kubemcpui.fortedigital.com
letsencryptEmail: danijel.simeunovic@fortedigital.com
trustedIPs: "172.16.1.0/24"
cloudProvider: upcloud

View File

@@ -96,10 +96,10 @@ You'll need read/write access to these repositories:
cd launchpad
```
2. **helm-values** (Values repo)
2. **helm-prod-values** (Values repo)
```bash
git clone https://git.forteapps.net/Forte/helm-prod-values.git
cd helm-values
cd helm-prod-values
```
3. **forte-helm** (Chart repo - read-only for most developers)
@@ -175,13 +175,13 @@ npm run dev
│ - GitHub Actions builds image │
│ - Pushes to container registry (GHCR, Docker Hub) │
│ - Tags with version (e.g., v2.0.4) │
│ - Updates helm-values repository with new tag │
│ - Updates helm-prod-values repository with new tag │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Step 3: GitOps Sync (Automated) │
│ - ArgoCD detects change in helm-values │
│ - ArgoCD detects change in helm-prod-values │
│ - Pulls updated configuration │
│ - Syncs to Kubernetes cluster │
│ - Sends Slack notification on success/failure │
@@ -201,7 +201,7 @@ Our setup uses three repositories:
| Repository | Purpose | Who Edits | How Often |
|------------|---------|-----------|-----------|
| **forte-helm** | Helm chart templates (generic, reusable) | Platform engineers | ❌ Rarely |
| **helm-values** | Application configuration (image tag, env vars) | Developers / CI pipelines | ✅ Sometimes |
| **helm-prod-values** | Application configuration (image tag, env vars) | Developers / CI pipelines | ✅ Sometimes |
| **launchpad** | ArgoCD Applications (what gets deployed) | Platform / DevOps engineers | ✅ Per new app |
### Example: Deploying "myapp"
@@ -223,7 +223,7 @@ spec:
value: {{ .Values.app.port }}
```
#### Repository: `helm-values` (Your App Config)
#### Repository: `helm-prod-values` (Your App Config)
```yaml
# myapp/values.yaml
# Your app's specific configuration
@@ -248,13 +248,13 @@ metadata:
namespace: argocd
spec:
sources:
- repoURL: https://github.com/fortedigital/forte-helm
- repoURL: https://git.forteapps.net/Forte/forte-helm
path: forteapp
helm:
valueFiles:
- $values/myapp/values.yaml
- repoURL: git@github.com:fortedigital/helm-values.git
- repoURL: git@github.com:fortedigital/helm-prod-values.git
ref: values
destination:
@@ -316,10 +316,10 @@ Ensure your app repository has:
docker build -t ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }} .
docker push ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }}
- name: Update helm-values
- name: Update helm-prod-values
run: |
git clone git@github.com:fortedigital/helm-values.git
cd helm-values
git clone git@github.com:fortedigital/helm-prod-values.git
cd helm-prod-values
mkdir -p hello-world
cat > hello-world/values.yaml <<EOF
app:
@@ -334,7 +334,7 @@ Ensure your app repository has:
### Step 2: Create Helm Values
Create a folder in `helm-values` repository:
Create a folder in `helm-prod-values` repository:
```bash
cd ~/dev/k8s/helm-prod-values
@@ -412,7 +412,7 @@ spec:
sources:
# Source 1: Helm chart templates
- repoURL: https://github.com/fortedigital/forte-helm
- repoURL: https://git.forteapps.net/Forte/forte-helm
path: forteapp
targetRevision: HEAD
helm:
@@ -420,7 +420,7 @@ spec:
- $values/hello-world/values.yaml
# Source 2: Helm values
- repoURL: git@github.com:fortedigital/helm-values.git
- repoURL: git@github.com:fortedigital/helm-prod-values.git
targetRevision: HEAD
ref: values
@@ -528,7 +528,7 @@ git push origin main
2. ✅ Builds new Docker image
3. ✅ Tags with new version (e.g., `v20260316-143022`)
4. ✅ Pushes to container registry
5. ✅ Updates `helm-values/myapp/values.yaml` with new tag
5. ✅ Updates `helm-prod-values/myapp/values.yaml` with new tag
6. ✅ ArgoCD detects change
7. ✅ Syncs new version to cluster
8. ✅ Sends Slack notification
@@ -683,7 +683,7 @@ git push
#### Step 4: Reference Secret in Application
Update your `helm-values/myapp/values.yaml`:
Update your `helm-prod-values/myapp/values.yaml`:
```yaml
app:
@@ -791,7 +791,7 @@ Three authentication modes are supported:
#### Step 1: Configure Helm Values
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
auth:
enabled: true
type: token # Token mode (default)
@@ -913,7 +913,7 @@ rm private/myapp-auth-oidc.yaml
#### Step 3: Configure Helm Values
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
auth:
enabled: true
type: oidc # OIDC mode
@@ -1049,7 +1049,7 @@ policies.forteapps.io/auth-image-version: "v1.2.3"
#### Example 1: Internal API with Token Auth
```yaml
# helm-values/internal-api/values.yaml
# helm-prod-values/internal-api/values.yaml
app:
image:
repository: ghcr.io/company/internal-api
@@ -1077,7 +1077,7 @@ curl -H "Authorization: Bearer d4f88f..." \
#### Example 2: User-Facing App with OIDC
```yaml
# helm-values/web-app/values.yaml
# helm-prod-values/web-app/values.yaml
app:
image:
repository: ghcr.io/company/web-app
@@ -1112,7 +1112,7 @@ kubectl create secret generic auth-oidc \
#### Example 3: MCP Server with OAuth 2.0
```yaml
# helm-values/mcp-server/values.yaml
# helm-prod-values/mcp-server/values.yaml
app:
image:
repository: ghcr.io/company/mcp-server
@@ -1136,7 +1136,7 @@ The MCP auth mode implements RFC 9728 (OAuth 2.0 Protected Resource Metadata) fo
#### Example 4: Disabling Authentication
```yaml
# helm-values/public-api/values.yaml
# helm-prod-values/public-api/values.yaml
auth:
enabled: false # No authentication
@@ -1364,7 +1364,7 @@ Existing clients (like Gitea) are defined directly in `forte-realm.json` inside
#### Step 1: Add Client to Realm Config
In `infra/values/keycloak-values.yaml`, add a new entry to the `clients` array in `forte-realm.json`:
In `infra/values/base/keycloak-values.yaml`, add a new entry to the `clients` array in `forte-realm.json`:
```json
{
@@ -1404,7 +1404,7 @@ existingSecret: myapp-oidc-credentials
```bash
cd ~/dev/k8s/launchpad
git add infra/values/keycloak-values.yaml
git add infra/values/base/keycloak-values.yaml
git commit -m "Add myapp Keycloak client with auto-sync"
git push
```
@@ -1500,7 +1500,7 @@ kubectl exec -n myapp <pod-name> -- env
# Check if secrets exist
kubectl get secrets -n myapp
# Increase resources in helm-values
# Increase resources in helm-prod-values
vim ~/dev/k8s/helm-prod-values/myapp/values.yaml
```
@@ -1649,7 +1649,7 @@ If you're stuck:
### Configuration Management
✅ **DO**:
- Keep configuration in `helm-values` repository
- Keep configuration in `helm-prod-values` repository
- Use environment variables for config
- Document what each value does
- Use reasonable resource limits

View File

@@ -16,7 +16,7 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
### Key Characteristics
- **Environment**: Production (internal use only)
- **Cluster Type**: Single cluster, single environment
- **Cluster Type**: Multi-cluster (upc-dev, upc-prod) via Kustomize overlays
- **GitOps Tool**: ArgoCD
- **Deployment Pattern**: App-of-Apps
- **Secret Management**: Sealed Secrets (kubeseal)
@@ -47,7 +47,7 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
│ │ │
│ │ │
└────────► Update image tag ─┴──────────────────────────┘
in helm-values │
in helm-prod-values │
┌────────────────────────────────┐
@@ -62,8 +62,8 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
┌────────────────────────────────┐
│ Kubernetes Cluster
│ (UpCloud Managed)
│ Kubernetes Clusters
│ (UpCloud: upc-dev, upc-prod)
│ │
│ ┌──────────────────────────┐ │
│ │ ArgoCD │ │
@@ -116,81 +116,75 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
```
launchpad/
├── bootstrap.sh # Cluster initialization script
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
├── _app-of-apps-upc-dev.yaml # Root ArgoCD Application (upc-dev cluster)
├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod cluster)
├── infra/ # Infrastructure ArgoCD Applications
│ ├── enterprise-apps.yaml # Parent app managing all apps in apps/
│ ├── cluster-resources-application.yaml
│ ├── traefik-application.yaml
│ ├── cert-manager-application.yaml
│ ├── kyverno.yaml
│ ├── kyverno-policies.yaml
│ ├── prometheus.yaml
│ ├── grafana.yaml
│ ├── loki.yaml
│ ├── tempo.yaml
├── fluent-bit.yaml
│ ├── trivy.yaml
│ ├── sealedsecrets.yaml
├── secrets.yaml
├── infra/ # Infrastructure ArgoCD Applications (Kustomize)
│ ├── base/ # Base Application manifests (upc-dev defaults)
│ ├── kustomization.yaml
│ ├── traefik-application.yaml
│ ├── keycloak.yaml
│ ├── grafana.yaml
│ ├── gitea.yaml
│ ├── gitea-actions.yaml
│ ├── tempo.yaml
│ ├── renovate.yaml
│ ├── ... # All other Application manifests
│ └── secrets.yaml
│ ├── overlays/ # Per-cluster overrides
│ ├── upc-dev/ # UpCloud Dev (uses base as-is)
│ └── upc-prod/ # UpCloud Prod (patches value paths)
│ ├── dashboards/ # Grafana dashboard ConfigMaps
│ └── values/ # Helm value overrides for infra
│ ├── argocd-values.yaml
├── prometheus-values.yaml
├── grafana-values.yaml
├── loki-values.yaml
├── tempo-values.yaml
└── fluent-bit-values.yaml
│ ├── base/ # Shared values (all clusters)
│ ├── traefik-values.yaml
│ ├── keycloak-values.yaml
│ ├── grafana-values.yaml
│ ├── prometheus-values.yaml
│ ├── gitea-values.yaml
│ │ └── ...
│ ├── upc-dev/ # upc-dev cluster-specific values
│ │ ├── traefik-values.yaml
│ │ ├── keycloak-values.yaml
│ │ └── grafana-values.yaml
│ └── upc-prod/ # upc-prod cluster-specific values
│ ├── traefik-values.yaml
│ ├── keycloak-values.yaml
│ └── grafana-values.yaml
├── apps/ # Business Application ArgoCD manifests
│ ├── mcp10x.yaml # MCP 10X application
│ ├── musicman.yaml # Music Man application
│ ├── dot-ai-stack.yaml # Dot AI Stack
│ └── argo-mcp.yaml # ArgoCD MCP server
├── apps/ # Business Application ArgoCD manifests (Kustomize)
│ ├── base/ # Base app manifests
│ ├── kustomization.yaml
│ ├── dot-ai-stack.yaml
│ └── ...
│ └── overlays/
│ ├── upc-dev/ # Uses base as-is
│ └── upc-prod/ # Patches value paths
├── cluster-resources/ # Cluster-wide Kubernetes resources
│ ├── cert-manager-namespace.yaml
│ ├── secrets-namespace.yaml
│ ├── letsencrypt-issuer.yaml # Let's Encrypt ClusterIssuer
│ ├── kyverno-config.yaml
│ ├── argocd-notifications-secret-sealed.yaml
│ ├── forte10x-repo-credentials-sealed.yaml
│ ├── mcp10x-repo-credentials-sealed.yaml
│ ├── ...
│ └── policies/ # Kyverno policies
│ ├── deployment-verifier.yaml
│ ├── label-checker.yaml
│ ├── bare-pod-cleaner.yaml
│ ├── replicaset-cleaner.yaml
│ ├── default-ns-blocker.yaml
│ ├── secret-cloner.yaml
│ └── auth-sidecar-injector.yaml
├── secrets/ # Application secrets (sealed)
── argocd-mcp-credentials.yaml
│ ├── dot-ai-secrets.yaml
│ ├── mcp10x-credentials-sealed.yaml
│ └── musicman-credentials.yaml
├── secrets/ # Application secrets (sealed, per-cluster)
── upc-dev/ # Secrets for upc-dev cluster
├── private/ # Local-only files (NOT in Git)
│ ├── *.yaml # Unsealed secrets
│ └── *.sh # Helper scripts
└── docs/ # Documentation
├── GITOPS-ARCHITECTURE.md # This file
├── DEVELOPER-GUIDE.md
├── OPERATIONS-RUNBOOK.md
└── REFERENCE.md
```
**Key Points**:
- `_app-of-apps.yaml` is the root Application that ArgoCD monitors
- `infra/enterprise-apps.yaml` auto-discovers all apps in `apps/` folder
- `_app-of-apps-upc-dev.yaml` and `_app-of-apps-upc-prod.yaml` are the per-cluster root Applications
- Kustomize overlays in `infra/overlays/` render base Applications with per-cluster patches
- Helm values are split: `values/base/` (shared) + `values/upc-dev/` or `values/upc-prod/` (cluster-specific)
- `apps/` follows the same base/overlays pattern for business applications
- Changes pushed to this repo trigger automatic syncs in ArgoCD
- `private/` folder contains local-only files (Git-ignored)
---
### 2. **Helm Charts Repository**
**Repository**: `https://github.com/fortedigital/forte-helm`
**Repository**: `https://git.forteapps.net/Forte/forte-helm`
**Purpose**: Reusable Helm chart templates for Forte applications
**Location**: `C:\dev\k8s\forte-helm`
@@ -224,7 +218,7 @@ forte-helm/
---
### 3. **Helm Values Repository**
**Repository**: `git@github.com:fortedigital/helm-values.git`
**Repository**: `git@github.com:fortedigital/helm-prod-values.git`
**Purpose**: Environment-specific configuration for each application
**Location**: `C:\dev\k8s\helm-prod-values`
@@ -234,8 +228,6 @@ helm-prod-values/
│ └── values.yaml # MCP 10X configuration
├── musicman/
│ └── values.yaml # Music Man configuration
├── mcpcoder/
│ └── values.yaml # MCP Coder configuration
└── argocd-mcp/
└── values.yaml # ArgoCD MCP configuration
```
@@ -285,7 +277,7 @@ app-repository/
2. Build Docker image
3. Tag with version (e.g., `v2.0.4`)
4. Push to container registry (GHCR, Docker Hub, etc.)
5. Update image tag in `helm-values` repository
5. Update image tag in `helm-prod-values` repository
6. ArgoCD detects change and syncs automatically
---
@@ -295,7 +287,7 @@ app-repository/
### The App-of-Apps Pattern
```
_app-of-apps.yaml (Root)
_app-of-apps-{upc-dev,upc-prod}.yaml (Root, per cluster)
├── infrastructure-apps (manages infra/)
│ ├── cluster-resources-application
@@ -315,10 +307,10 @@ _app-of-apps.yaml (Root)
```
**How It Works**:
1. Bootstrap script installs ArgoCD and applies `_app-of-apps.yaml`
2. ArgoCD creates the root Application which monitors `infra/` folder
3. Each YAML in `infra/` becomes a child Application
4. `enterprise-apps.yaml` monitors `apps/` folder and auto-discovers applications
1. Bootstrap script installs ArgoCD and applies `_app-of-apps-upc-dev.yaml` (or `upc-prod`)
2. ArgoCD creates the root Application which monitors the appropriate `infra/overlays/` folder
3. Kustomize renders base Applications with cluster-specific patches
4. `enterprise-apps` Application monitors the cluster's `apps/overlays/` folder
5. ArgoCD continuously syncs (every 60s) and auto-heals drift
### Sync Waves & Ordering
@@ -346,13 +338,13 @@ Applications like `mcp10x` and `musicman` use multiple sources:
```yaml
spec:
sources:
- repoURL: https://github.com/fortedigital/forte-helm
- repoURL: https://git.forteapps.net/Forte/forte-helm
path: forteapp # Helm chart templates
helm:
valueFiles:
- $values/mcp10x/values.yaml # Reference to second source
- repoURL: git@github.com:fortedigital/helm-values.git
- repoURL: git@github.com:fortedigital/helm-prod-values.git
targetRevision: HEAD
ref: values # Named reference
```
@@ -363,6 +355,34 @@ spec:
- Easy to update all apps by changing the chart
- Environment-specific values isolated in separate repo
### Multi-Cluster Pattern
Kustomize overlays enable deploying the same Applications across clusters with different configurations:
```yaml
# infra/base/ contains default (upc-dev) Applications
# Helm values are layered: base + cluster-specific
valueFiles:
- $values/infra/values/base/traefik-values.yaml # Shared config
- $values/infra/values/upc-dev/traefik-values.yaml # Cluster-specific
# infra/overlays/upc-prod/kustomization.yaml patches the second valueFile
patches:
- target:
kind: Application
name: traefik
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/traefik-values.yaml
```
**Benefits**:
- Single source of truth for Application definitions
- Cluster-specific values isolated per overlay
- Easy to add new clusters by creating a new overlay
- Base values shared across all clusters reduce duplication
---
## CI/CD Pipeline
@@ -392,8 +412,8 @@ jobs:
- name: Update Helm values
run: |
git clone git@github.com:fortedigital/helm-values.git
cd helm-values/app
git clone git@github.com:fortedigital/helm-prod-values.git
cd helm-prod-values/app
sed -i "s/tag: .*/tag: $VERSION/" values.yaml
git commit -am "Update app to $VERSION"
git push
@@ -410,7 +430,7 @@ jobs:
- Syncs application to cluster
2. **Helm Values Change**:
- CI/CD updates `helm-values/myapp/values.yaml`
- CI/CD updates `helm-prod-values/myapp/values.yaml`
- ArgoCD detects change
- Pulls new Helm chart with updated values
- Applies to cluster
@@ -617,7 +637,7 @@ Notifications include:
✅ **DO**:
- Follow the `forteapp` chart pattern
- Use semantic versioning for image tags
- Update helm-values via CI/CD
- Update helm-prod-values via CI/CD
- Test locally with Docker Compose
- Document environment variables

View File

@@ -85,7 +85,8 @@ kubectl get applications -n argocd
1. **Configure DNS** for ingress domains:
- `argocd.127.0.0.1.nip.io` (local dev)
- `*.forteapps.net` (production)
- `*.forteapps.net` (dev)
- `*.fortedigital.com` (production)
2. **Verify Let's Encrypt certificates**:
```bash
@@ -107,7 +108,7 @@ kubectl get applications -n argocd
### ArgoCD Repository Access Setup
ArgoCD needs SSH access to private Git repositories to pull manifests and Helm values. This section covers setting up deploy keys for GitHub repositories.
ArgoCD needs SSH access to private Git repositories to pull manifests and Helm values. This section covers setting up deploy keys for Gitea repositories.
#### Why Deploy Keys?
@@ -119,7 +120,7 @@ ArgoCD needs SSH access to private Git repositories to pull manifests and Helm v
#### Prerequisites
- kubectl access to the cluster
- Write access to the GitHub repository
- Write access to the Gitea repository
- ArgoCD installed and running
#### Setup Procedure
@@ -138,16 +139,16 @@ ssh-keygen -t rsa -b 4096 -C "argocd-deploy-key-launchpad" -f argocd-deploy-key
This creates two files:
- `argocd-deploy-key` - Private key (keep secret)
- `argocd-deploy-key.pub` - Public key (add to GitHub)
- `argocd-deploy-key.pub` - Public key (add to Gitea)
**Step 2: Add Public Key to GitHub**
**Step 2: Add Public Key to Gitea**
1. Copy the public key:
```bash
cat argocd-deploy-key.pub
```
2. Go to GitHub repository settings:
2. Go to Gitea repository settings:
- Navigate to: `https://git.forteapps.net/Forte/launchpad/settings/keys`
- Or: Repository → Settings → Deploy keys
@@ -157,12 +158,12 @@ This creates two files:
- ☐ Allow write access (leave unchecked - read-only is sufficient)
- Click **"Add key"**
4. Repeat for the `helm-values` repository if it's private:
4. Repeat for the `helm-prod-values` repository if it's private:
```bash
# Generate separate key for helm-values repo
ssh-keygen -t ed25519 -C "argocd-deploy-key-helm-values" -f argocd-helm-values-key -N ""
# Generate separate key for helm-prod-values repo
ssh-keygen -t ed25519 -C "argocd-deploy-key-helm-prod-values" -f argocd-helm-prod-values-key -N ""
# Add to: https://github.com/fortedigital/helm-values/settings/keys
# Add to: https://git.forteapps.net/Forte/helm-prod-values/settings/keys
```
**Step 3: Create Kubernetes Secret**
@@ -207,7 +208,7 @@ kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
# Settings → Repositories → Should show "Successful" status
# Test by creating an application
kubectl apply -f _app-of-apps.yaml
kubectl apply -f _app-of-apps-upc-dev.yaml # or _app-of-apps-upc-prod.yaml
# Check application sync status
kubectl get applications -n argocd
@@ -270,7 +271,7 @@ rm /tmp/test-repo-access.yaml
# Generate new key
ssh-keygen -t ed25519 -C "argocd-deploy-key-$(date +%Y%m)" -f argocd-new-key -N ""
# Add new public key to GitHub (keep old key for now)
# Add new public key to Gitea (keep old key for now)
# Update Kubernetes secret
kubectl create secret generic repo-launchpad \
@@ -278,7 +279,7 @@ rm /tmp/test-repo-access.yaml
--namespace=argocd \
--dry-run=client -o yaml | kubectl apply -f -
# Test access, then remove old deploy key from GitHub
# Test access, then remove old deploy key from Gitea
# Clean up
shred -u argocd-new-key
@@ -289,7 +290,7 @@ rm /tmp/test-repo-access.yaml
# List all repository secrets
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
# Review deploy keys in GitHub
# Review deploy keys in Gitea
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
```
@@ -312,16 +313,16 @@ kubectl get secret repo-launchpad -n argocd -o yaml | grep argocd.argoproj.io/se
# Check ArgoCD application controller logs
kubectl logs -n argocd deployment/argocd-application-controller | grep -i "permission denied"
# Verify deploy key is added to GitHub
# Verify deploy key is added to Gitea
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
```
**Issue: "Host key verification failed"**
```bash
# Add GitHub to known_hosts
# Add Gitea to known_hosts
kubectl exec -n argocd deployment/argocd-repo-server -- \
ssh-keyscan github.com >> ~/.ssh/known_hosts
ssh-keyscan git.forteapps.net >> ~/.ssh/known_hosts
# Or disable strict host key checking (less secure)
kubectl patch secret repo-launchpad -n argocd \
@@ -346,16 +347,16 @@ kubectl rollout restart deployment argocd-application-controller -n argocd
#### Multiple Repository Setup
For the three-repository pattern (launchpad, forte-helm, helm-values):
For the three-repository pattern (launchpad, forte-helm, helm-prod-values):
```bash
# 1. launchpad (main config repo)
ssh-keygen -t ed25519 -C "argocd-launchpad" -f key-sturdy -N ""
# Add key-sturdy.pub to: https://git.forteapps.net/Forte/launchpad/settings/keys
# 2. helm-values (private values repo)
ssh-keygen -t ed25519 -C "argocd-helm-values" -f key-helm-values -N ""
# Add key-helm-values.pub to: https://github.com/fortedigital/helm-values/settings/keys
# 2. helm-prod-values (private values repo)
ssh-keygen -t ed25519 -C "argocd-helm-prod-values" -f key-helm-prod-values -N ""
# Add key-helm-prod-values.pub to: https://git.forteapps.net/Forte/helm-prod-values/settings/keys
# 3. forte-helm (private helm charts repo)
@@ -366,14 +367,14 @@ kubectl create secret generic repo-launchpad \
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
kubectl apply -f -
kubectl create secret generic repo-helm-values \
--from-file=sshPrivateKey=key-helm-values \
kubectl create secret generic repo-helm-prod-values \
--from-file=sshPrivateKey=key-helm-prod-values \
--namespace=argocd --dry-run=client -o yaml | \
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
kubectl apply -f -
# Clean up keys
shred -u key-sturdy key-helm-values
shred -u key-sturdy key-helm-prod-values
```
#### Converting HTTPS to SSH
@@ -390,7 +391,7 @@ If you're currently using HTTPS and want to switch to SSH:
# repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
# 3. Update and commit
find . -name "*.yaml" -type f -exec sed -i 's|https://github.com/fortedigital/|git@github.com:fortedigital/|g' {} +
find . -name "*.yaml" -type f -exec sed -i 's|https://git.forteapps.net/Forte/|git@git.forteapps.net:Forte/|g' {} +
git add .
git commit -m "Switch from HTTPS to SSH for repository access"
@@ -494,7 +495,7 @@ spec:
See [Developer Guide](DEVELOPER-GUIDE.md#deploying-your-first-application) for detailed steps.
**Quick checklist:**
- [ ] Create `helm-values/myapp/values.yaml`
- [ ] Create `helm-prod-values/myapp/values.yaml`
- [ ] Create `apps/myapp.yaml` in config repo
- [ ] Create SealedSecret if needed
- [ ] Commit and push changes
@@ -559,7 +560,7 @@ kubectl scale deployment myapp -n myapp --replicas=3
#### GitOps Scaling
Update `helm-values/myapp/values.yaml`:
Update `helm-prod-values/myapp/values.yaml`:
```yaml
app:
@@ -573,7 +574,7 @@ Commit and push - ArgoCD will sync.
Enable Horizontal Pod Autoscaler:
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
app:
hpa:
enabled: true
@@ -622,7 +623,7 @@ kubectl rollout undo deployment myapp -n myapp
#### Option 3: Change Image Tag
```bash
# Edit helm-values
# Edit helm-prod-values
cd ~/dev/k8s/helm-prod-values
vim myapp/values.yaml
@@ -642,7 +643,7 @@ git push
#### Update Resource Limits
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
app:
resources:
requests:
@@ -656,7 +657,7 @@ app:
#### Enable Database
```yaml
# In helm-values/myapp/values.yaml
# In helm-prod-values/myapp/values.yaml
db:
enabled: true
persistence:
@@ -1266,7 +1267,7 @@ spec:
**What Needs Backup**:
- ❌ Cluster state (not backed up - recreate via GitOps)
- ❌ Persistent volumes (currently not critical)
- ✅ Git repositories (GitHub provides backup)
- ✅ Git repositories (Gitea provides backup)
- ⚠️ Secrets (sealed secrets in Git, unseal keys need safekeeping)
### Cluster Rebuild
@@ -1352,13 +1353,13 @@ kubectl get deployment argocd-server -n argocd \
-o jsonpath='{.spec.template.spec.containers[0].image}'
# Update version in values
vim infra/values/argocd-values.yaml
vim infra/values/base/argocd-values.yaml
# Or upgrade via Helm directly
helm upgrade argocd argo-cd \
--repo https://argoproj.github.io/argo-helm \
--namespace argocd \
--values infra/values/argocd-values.yaml \
--values infra/values/base/argocd-values.yaml \
--version 6.0.0 # New version
# Verify
@@ -1454,8 +1455,8 @@ kubectl top pods --all-namespaces --sort-by=cpu
Example: Adding Redis
```bash
# 1. Create application manifest
cat > infra/redis-application.yaml <<EOF
# 1. Create application manifest in base/
cat > infra/base/redis-application.yaml <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
@@ -1465,15 +1466,17 @@ metadata:
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
source:
repoURL: https://charts.bitnami.com/bitnami
sources:
- repoURL: https://charts.bitnami.com/bitnami
chart: redis
targetRevision: 18.0.0
helm:
values: |
auth:
enabled: true
password: changeme
releaseName: redis
valueFiles:
- \$values/infra/values/base/redis-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: redis
@@ -1485,30 +1488,37 @@ spec:
- CreateNamespace=true
EOF
# 2. Commit and push
git add infra/redis-application.yaml
# 2. Add to base kustomization
# Edit infra/base/kustomization.yaml and add: - redis-application.yaml
# 3. Create base values file
cat > infra/values/base/redis-values.yaml <<EOF
auth:
enabled: true
EOF
# 4. Commit and push
git add infra/base/redis-application.yaml infra/values/base/redis-values.yaml infra/base/kustomization.yaml
git commit -m "Add Redis infrastructure component"
git push
# 3. ArgoCD will auto-sync within 60 seconds
# 5. ArgoCD will auto-sync within 60 seconds
```
### Multi-Cluster Setup (Future)
### Multi-Cluster Setup
For multi-cluster deployments:
The repository supports multiple clusters via Kustomize overlays:
```yaml
# Different destinations per environment
# dev-cluster
destination:
server: https://dev.k8s.example.com
namespace: myapp
- **upc-dev** (default): `infra/overlays/upc-dev/` — uses base Applications as-is
- **upc-prod**: `infra/overlays/upc-prod/` — patches value file paths from `upc-dev` to `upc-prod`
# prod-cluster
destination:
server: https://prod.k8s.example.com
namespace: myapp
```
Each cluster has its own:
- Root app-of-apps file: `_app-of-apps-upc-dev.yaml` / `_app-of-apps-upc-prod.yaml`
- Cluster-specific Helm values: `infra/values/upc-dev/` / `infra/values/upc-prod/`
- Sealed secrets: `secrets/upc-dev/` (others as needed)
- Apps overlay: `apps/overlays/upc-dev/` / `apps/overlays/upc-prod/`
To add a new cluster, create a new overlay directory (e.g., `infra/overlays/upc-staging/`) with patches that swap the value file paths.
### Blue-Green Deployments
@@ -1552,7 +1562,7 @@ git push
kubectl scale deployment myapp -n myapp --replicas=0
# Update Git
vim helm-values/myapp/values.yaml
vim helm-prod-values/myapp/values.yaml
# Set replicaCount: 0
git commit -am "Scale down myapp for maintenance"
git push
@@ -1625,7 +1635,7 @@ echo "Remember to delete: $SECRET_FILE"
- [ ] Application code repository created
- [ ] Dockerfile created and tested
- [ ] GitHub Actions workflow configured
- [ ] Gitea Actions workflow configured
- [ ] Helm values created in `helm-prod-values/`
- [ ] ArgoCD application manifest created in `apps/`
- [ ] Secrets created and sealed

View File

@@ -180,7 +180,7 @@ Reference for:
┌──────────────────────────────────────────────────────────────┐
Kubernetes Cluster (UpCloud)
│ Kubernetes Clusters (UpCloud: upc-dev, upc-prod)
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Infrastructure: Traefik, Cert-Manager, Kyverno │ │
│ ├──────────────────────────────────────────────────────┤ │
@@ -194,7 +194,7 @@ Reference for:
### Key Technologies
- **GitOps**: ArgoCD
- **Kubernetes**: UpCloud Managed Kubernetes
- **Kubernetes**: UpCloud Managed Kubernetes (multi-cluster: upc-dev, upc-prod)
- **Ingress**: Traefik v2
- **Certificates**: Cert-Manager + Let's Encrypt
- **Policies**: Kyverno

View File

@@ -21,7 +21,7 @@
|-----------|-------|
| **Provider** | UpCloud Managed Kubernetes |
| **Environment** | Production (internal use) |
| **Cluster Count** | Single cluster |
| **Cluster Count** | Multi-cluster (upc-dev, upc-prod) |
| **GitOps Tool** | ArgoCD |
| **Ingress Controller** | Traefik v2 |
| **Certificate Management** | Cert-Manager + Let's Encrypt |
@@ -71,7 +71,8 @@ Internet
```
launchpad/
├── bootstrap.sh # Cluster initialization script
├── _app-of-apps.yaml # Root ArgoCD Application
├── _app-of-apps-upc-dev.yaml # Root ArgoCD Application (upc-dev)
├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod)
├── infra/ # Infrastructure applications
│ ├── cluster-resources-application.yaml
@@ -156,15 +157,15 @@ ArgoCd() {
helm upgrade --install argocd argo-cd \
--repo https://argoproj.github.io/argo-helm \
--namespace argocd --create-namespace \
--values infra/values/argocd-values.yaml \
--values infra/values/base/argocd-values.yaml \
--set notifications.context.clusterName="$CLUSTER_NAME" \
--timeout 60s --atomic
kubectl apply -f _app-of-apps.yaml -n argocd
kubectl apply -f _app-of-apps-upc-dev.yaml -n argocd # or _app-of-apps-upc-prod.yaml
}
```
**`_app-of-apps.yaml`**
**`_app-of-apps-upc-dev.yaml`** / **`_app-of-apps-upc-prod.yaml`**
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
@@ -189,7 +190,7 @@ spec:
### Helm Charts Repository: `forte-helm`
**URL**: `https://github.com/fortedigital/forte-helm`
**URL**: `https://git.forteapps.net/Forte/forte-helm`
#### Chart: `forteapp`
@@ -336,20 +337,18 @@ configmap: [] # Application ConfigMap key-value pairs
---
### Helm Values Repository: `helm-values`
### Helm Values Repository: `helm-prod-values`
**URL**: `https://github.com/fortedigital/helm-values.git`
**URL**: `https://git.forteapps.net/Forte/helm-prod-values.git`
#### Structure
```
helm-values/
helm-prod-values/
├── mcp10x/
│ └── values.yaml
├── musicman/
│ └── values.yaml
├── mcpcoder/
│ └── values.yaml
└── argocd-mcp/
└── values.yaml
```
@@ -525,14 +524,14 @@ spec:
# Multi-source configuration
sources:
- repoURL: https://github.com/fortedigital/forte-helm
- repoURL: https://git.forteapps.net/Forte/forte-helm
path: forteapp
targetRevision: HEAD
helm:
valueFiles:
- $values/<app-name>/values.yaml
- repoURL: git@github.com:fortedigital/helm-values.git
- repoURL: git@github.com:fortedigital/helm-prod-values.git
targetRevision: HEAD
ref: values
@@ -615,7 +614,7 @@ retry:
**Configuration**:
```yaml
# infra/traefik-application.yaml
# infra/base/traefik-application.yaml
replicas: 2
service:
@@ -790,7 +789,7 @@ persistence:
**Configuration**:
```yaml
# infra/gitea.yaml + infra/values/gitea-values.yaml
# infra/base/gitea.yaml + infra/values/base/gitea-values.yaml
ingress:
host: git.forteapps.net
tls: cert-manager (letsencrypt-prod)
@@ -816,12 +815,19 @@ postgresql:
**Authentication**: Keycloak OIDC via `forte` realm (client ID: `gitea`). Protocol mapper: `email_verified` hardcoded claim (`true`, boolean) on ID token, Access token, and Userinfo.
**External User Sync**: Disabled (`cron.sync_external_users.ENABLED: false`). This Gitea cron job is designed for LDAP and deactivates OIDC-only users because it cannot enumerate them — causing "Sign-in prohibited" errors after the sync runs.
**Email Notifications**: Enabled (`ENABLE_NOTIFY_MAIL: true`). SMTP credentials injected via `gitea-smtp-secret` using `additionalConfigFromEnvs` with `GITEA__mailer__USER` / `GITEA__mailer__PASSWD` environment variables.
**Endpoints**:
- Web UI: `https://git.forteapps.net`
- SSH: port 22 (ClusterIP)
- Metrics: `/metrics` (Prometheus scrape)
**Secrets**: `gitea-credentials` (SealedSecret) containing `admin-password`, `postgres-password`, `secret` (OIDC client secret)
**Secrets**:
- `gitea-credentials` (SealedSecret) — admin password
- `gitea-oidc-credentials` (registrar-managed) — OIDC client ID + secret
- `gitea-smtp-secret` (SealedSecret) — SMTP username + password
### Gitea Actions Runners
@@ -833,7 +839,7 @@ postgresql:
**Configuration**:
```yaml
# infra/gitea-actions.yaml + infra/values/gitea-actions-values.yaml
# infra/base/gitea-actions.yaml + infra/values/base/gitea-actions-values.yaml
replicaCount: 3
runner:
@@ -870,6 +876,84 @@ dind:
- Gitea admin panel (`/admin/runners`) — runners show as Online
- Create test workflow in `.gitea/workflows/test.yml` — job executes
### AI Code Review (ai-review)
**Type**: Gitea Actions workflow (`.gitea/workflows/ai-review.yaml`)
**Trigger**: `pull_request` events (`opened`, `synchronize`)
**Runner**: `ubuntu-latest` (container: `nikitafilonov/ai-review:latest`)
**Purpose**: Automated AI-powered code review on pull requests using Claude (Anthropic). Posts inline comments on changed lines and a PR summary comment highlighting infrastructure impact.
**Architecture**:
- Uses [xai-review](https://github.com/nicktechnologies/xai-review) Docker image
- Shared configuration and prompts live in the `shared-prompts` Git submodule (→ `Forte/ai-review-prompts`)
- Review mode: `ONLY_ADDED_WITH_CONTEXT` — reviews only new/changed lines plus surrounding context (token-efficient)
- Agent mode: disabled (one-shot review, no multi-turn reasoning)
- LLM: Claude Sonnet (`claude-sonnet-4-20250514`)
**Shared Prompts Structure** (submodule: `Forte/ai-review-prompts`):
```
shared-prompts/
base/
security.md # org-wide security rules (all profiles)
iac/
.ai-review.yaml # IaC/GitOps profile config
inline.md # inline review prompt
summary.md # PR summary prompt
# future profiles: backend/, frontend/, etc.
```
**Configuration** (`shared-prompts/iac/.ai-review.yaml`):
```yaml
llm:
provider: CLAUDE
model: claude-sonnet-4-20250514
vcs:
provider: GITEA
review:
mode: ONLY_ADDED_WITH_CONTEXT
agent:
enabled: false
prompt:
inline_prompt_files: # concatenated in order
- ./shared-prompts/base/security.md
- ./shared-prompts/iac/inline.md
summary_prompt_files:
- ./shared-prompts/iac/summary.md
ignore:
- "*.sealed.yaml"
- "*.lock"
- "docs/**"
```
**Custom Prompts** (IaC profile):
- `shared-prompts/base/security.md` — org-wide security rules, concatenated before every inline review prompt
- `shared-prompts/iac/inline.md` — IaC-specific inline review (YAML, Helm, K8s manifests, shell scripts), max 7 comments
- `shared-prompts/iac/summary.md` — PR summary: affected services/namespaces, infrastructure impact, security flags
**Prompt composition**: ai-review does not support Jinja includes. Instead, list multiple files under `inline_prompt_files` / `summary_prompt_files` — they are concatenated in order with double newlines.
**Adding a new profile**: Create a new directory (e.g., `backend/`) with its own `.ai-review.yaml`, `inline.md`, and `summary.md`. The `inline_prompt_files` list should include `base/security.md` first, then the profile-specific prompt. Reference it in the consuming repo's workflow: `AI_REVIEW_CONFIG_FILE_YAML=./shared-prompts/backend/.ai-review.yaml`
**Required Secrets** (configure in Gitea repo or org settings):
| Secret | Purpose |
|--------|---------|
| `ANTHROPIC_API_KEY` | Claude API key (from Anthropic console) |
| `AI_REVIEW_TOKEN` | Gitea API token with `write:issue` + `read:repository` scopes (use a bot/service account) |
**Setup Steps**:
1. Create a Gitea bot/service account and generate an API token with `write:issue` + `read:repository` scopes
2. Add `AI_REVIEW_TOKEN` secret in Gitea repo settings → Actions → Secrets
3. Add `ANTHROPIC_API_KEY` secret with your Anthropic API key
4. Ensure the `shared-prompts` submodule is initialized (`git submodule update --init`)
5. Push the workflow file — it triggers automatically on PR creation/update
**Verification**:
- Open a PR with infrastructure changes → workflow runs → inline comments + summary appear
- Check Gitea Actions tab for workflow run status and logs
- Monitor Anthropic usage dashboard for token consumption
### Keycloak Client Registrar
**Type**: CronJob (deployed via Keycloak Helm chart `extraDeploy`)
@@ -994,7 +1078,7 @@ kubectl get secret keycloak-client-<app> -n keycloak -o jsonpath='{.metadata.ann
**Configuration**:
```yaml
# infra/renovate.yaml + infra/values/renovate-values.yaml
# infra/base/renovate.yaml + infra/values/base/renovate-values.yaml
cronjob:
schedule: "@daily"
concurrencyPolicy: Forbid

View File

@@ -18,7 +18,7 @@ spec:
source:
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
path: apps
path: apps/overlays/upc-dev
destination:
server: https://kubernetes.default.svc
namespace: apps

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: fluent-bit
valueFiles:
- $values/infra/values/fluent-bit-values.yaml
- $values/infra/values/base/fluent-bit-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: gitea-actions
valueFiles:
- $values/infra/values/gitea-actions-values.yaml
- $values/infra/values/base/gitea-actions-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: gitea
valueFiles:
- $values/infra/values/gitea-values.yaml
- $values/infra/values/base/gitea-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,7 +21,8 @@ spec:
helm:
releaseName: grafana
valueFiles:
- $values/infra/values/grafana-values.yaml
- $values/infra/values/base/grafana-values.yaml
- $values/infra/values/upc-dev/grafana-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,7 +21,8 @@ spec:
helm:
releaseName: keycloak
valueFiles:
- $values/infra/values/keycloak-values.yaml
- $values/infra/values/base/keycloak-values.yaml
- $values/infra/values/upc-dev/keycloak-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -0,0 +1,24 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- traefik-application.yaml
- keycloak.yaml
- grafana.yaml
- cert-manager-application.yaml
- kyverno.yaml
- sealedsecrets.yaml
- prometheus.yaml
- loki.yaml
- fluent-bit.yaml
- trivy.yaml
- enterprise-apps.yaml
- cluster-resources-application.yaml
- kyverno-policies.yaml
- secrets.yaml
- gitea.yaml
- gitea-actions.yaml
- opencost.yaml
- renovate.yaml
- tempo.yaml
- grafana-dashboards.yaml
- network-policies-application.yaml

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: loki
valueFiles:
- $values/infra/values/loki-values.yaml
- $values/infra/values/base/loki-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,9 +21,9 @@ spec:
helm:
releaseName: opencost
valueFiles:
- $values/infra/values/opencost-values.yaml
- $values/infra/values/base/opencost-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
targetRevision: HEAD
ref: values

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: prometheus
valueFiles:
- $values/infra/values/prometheus-values.yaml
- $values/infra/values/base/prometheus-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: renovate
valueFiles:
- $values/infra/values/renovate-values.yaml
- $values/infra/values/base/renovate-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -18,7 +18,7 @@ spec:
project: default
source:
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
path: secrets
path: secrets/upc-dev
destination:
server: https://kubernetes.default.svc
namespace: secrets

View File

@@ -21,7 +21,7 @@ spec:
helm:
releaseName: tempo
valueFiles:
- $values/infra/values/tempo-values.yaml
- $values/infra/values/base/tempo-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD

View File

@@ -0,0 +1,51 @@
apiVersion: v1
kind: Namespace
metadata:
name: traefik-system
annotations:
argocd.argoproj.io/sync-wave: "-1"
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: traefik
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/name: traefik
app.kubernetes.io/part-of: platform
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: https://traefik.github.io/charts
chart: traefik
targetRevision: "28.0.0"
helm:
releaseName: traefik
valueFiles:
- $values/infra/values/base/traefik-values.yaml
- $values/infra/values/upc-dev/traefik-values.yaml
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: traefik-system
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true

View File

@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
# No patches needed — base already has "upc-dev" paths
# upc-dev is the default/base cluster

View File

@@ -0,0 +1,50 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
# Traefik: swap upc-dev → upc-prod in valueFiles
- target:
kind: Application
name: traefik
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/traefik-values.yaml
# Keycloak: swap upc-dev → upc-prod
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/keycloak-values.yaml
# Grafana: swap upc-dev → upc-prod
- target:
kind: Application
name: grafana
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/grafana-values.yaml
# Secrets: change path to upc-prod
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/upc-prod
# Enterprise-apps: point to upc-prod overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/upc-prod

View File

@@ -1,159 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: traefik-system
annotations:
argocd.argoproj.io/sync-wave: "-1"
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: traefik
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/name: traefik
app.kubernetes.io/part-of: platform
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://traefik.github.io/charts
chart: traefik
targetRevision: "28.0.0"
helm:
values: |
metrics:
addInternals: true
tracing:
otlp:
enabled: true
logs:
general:
level: DEBUG
access:
format: json
enabled: true
additionalArguments:
- "--tracing.otlp.http.endpoint=http://tempo.monitoring.svc.cluster.local:4318/v1/traces"
providers:
kubernetesIngress:
publishedService: # Fixes ArgoCD health checks for LoadBalancer services
enabled: true
deployment:
replicas: 2
ingressRoute:
dashboard:
enabled: true
# Optional: specify entrypoint
entrypoint: traefik
api:
dashboard: true
debug: false
service:
type: LoadBalancer
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.priority: "42"
traefik.ingress.kubernetes.io/router.tls: "true"
service.beta.kubernetes.io/upcloud-load-balancer-config: |
{
"frontends": [
{
"name": "web",
"mode": "tcp"
},
{
"name": "websecure",
"mode": "tcp"
},
{
"name": "giteassh",
"mode": "tcp"
}
],
"backends": [
{
"name": "web",
"properties": {
"outbound_proxy_protocol": "v2"
}
},
{
"name": "websecure",
"properties": {
"outbound_proxy_protocol": "v2"
}
},
{
"name": "giteassh"
}
]
}
ingressClass:
enabled: true
isDefaultClass: true
# Configure entry points
ports:
metrics:
expose:
default: true
observability:
accessLogs: true
metrics: true
tracing: true
traceVerbosity: detailed
web:
proxyProtocol:
trustedIPs: "172.16.1.0/24"
forwardedHeaders:
trustedIPs: "172.16.1.0/24"
http:
redirections:
entrypoint:
to: websecure
scheme: https
websecure:
proxyProtocol:
trustedIPs: "172.16.1.0/24"
forwardedHeaders:
trustedIPs: "172.16.1.0/24"
observability:
accessLogs: true
metrics: true
tracing: true
giteassh:
port: 2222
expose:
default: true
exposedPort: 2222
protocol: TCP
destination:
server: https://kubernetes.default.svc
namespace: traefik-system
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true

View File

@@ -1,5 +1,3 @@
global:
domain: argocd.127.0.0.1.nip.io
configs:
secret:
createSecret: true
@@ -22,10 +20,6 @@ notifications:
secret:
create: false
# Shared context variables available in all templates
context:
clusterName: "dev-fd-no-svg1"
# Define notification templates
templates:
template.app-syncing: |

View File

@@ -0,0 +1,11 @@
dot-ai:
ingress:
enabled: true
className: traefik
dot-ai-ui:
uiAuth:
secretRef:
name: dot-ai-secrets
ingress:
enabled: true
className: traefik

View File

@@ -1,7 +1,5 @@
ingress:
enabled: true
hosts:
- grafana.127.0.0.1.nip.io
resources:
requests:
cpu: 50m

View File

@@ -1,5 +1,4 @@
# Bitnami Keycloak Helm Chart Values
# Host: id.forteapps.net
# Chart version: 25.2.0
image:
@@ -15,7 +14,6 @@ auth:
ingress:
enabled: true
hostname: id.forteapps.net
tls: true
ingressClassName: traefik
annotations:

View File

@@ -0,0 +1,75 @@
providers:
kubernetesIngress:
publishedService: # Fixes ArgoCD health checks for LoadBalancer services
enabled: true
kubernetesCRD:
allowCrossNamespace: true
deployment:
replicas: 2
ingressRoute:
dashboard:
enabled: true
# Optional: specify entrypoint
entrypoint: traefik
api:
dashboard: true
debug: false
service:
type: LoadBalancer
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.priority: "42"
traefik.ingress.kubernetes.io/router.tls: "true"
ingressClass:
enabled: true
isDefaultClass: true
# Configure entry points
ports:
metrics:
expose:
default: true
observability:
accessLogs: true
metrics: true
tracing: true
traceVerbosity: detailed
web:
http:
redirections:
entrypoint:
to: websecure
scheme: https
websecure:
observability:
accessLogs: true
metrics: true
tracing: true
gitea-ssh:
port: 2222
expose:
default: true
exposedPort: 2222
protocol: TCP
# -- IngressRouteTCP for Gitea SSH (cross-namespace to gitea/gitea-ssh service)
extraObjects:
- apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: gitea-ssh
spec:
entryPoints:
- gitea-ssh
routes:
- match: HostSNI(`*`)
services:
- name: gitea-ssh
namespace: gitea
port: 22

View File

@@ -0,0 +1,5 @@
global:
domain: argocd.127.0.0.1.nip.io
notifications:
context:
clusterName: "dev-fd-eu-no-svg1"

View File

@@ -0,0 +1,8 @@
dot-ai:
ingress:
host: kubemcp.forteapps.net
webUI:
baseUrl: http://kubemcpui.forteapps.net
dot-ai-ui:
ingress:
host: kubemcpui.forteapps.net

View File

@@ -0,0 +1,3 @@
ingress:
hosts:
- grafana.forteapps.net

View File

@@ -0,0 +1,2 @@
ingress:
hostname: id.forteapps.net

View File

@@ -0,0 +1,47 @@
service:
annotations:
service.beta.kubernetes.io/upcloud-load-balancer-config: |
{
"frontends": [
{
"name": "web",
"mode": "tcp"
},
{
"name": "websecure",
"mode": "tcp"
},
{
"name": "gitea-ssh",
"mode": "tcp"
}
],
"backends": [
{
"name": "web",
"properties": {
"outbound_proxy_protocol": "v2"
}
},
{
"name": "websecure",
"properties": {
"outbound_proxy_protocol": "v2"
}
},
{
"name": "gitea-ssh"
}
]
}
ports:
web:
proxyProtocol:
trustedIPs: "172.16.1.0/24"
forwardedHeaders:
trustedIPs: "172.16.1.0/24"
websecure:
proxyProtocol:
trustedIPs: "172.16.1.0/24"
forwardedHeaders:
trustedIPs: "172.16.1.0/24"

View File

@@ -0,0 +1,5 @@
global:
domain: argocd.fortedigital.com
notifications:
context:
clusterName: "prod-fd-no-svg1"

View File

@@ -0,0 +1,8 @@
dot-ai:
ingress:
host: kubemcp.fortedigital.com
webUI:
baseUrl: http://kubemcpui.fortedigital.com
dot-ai-ui:
ingress:
host: kubemcpui.fortedigital.com

View File

@@ -0,0 +1,3 @@
ingress:
hosts:
- grafana.fortedigital.com

View File

@@ -0,0 +1,2 @@
ingress:
hostname: id.fortedigital.com

View File

@@ -0,0 +1,13 @@
service:
annotations: {}
ports:
web:
proxyProtocol:
trustedIPs: "10.0.0.0/16"
forwardedHeaders:
trustedIPs: "10.0.0.0/16"
websecure:
proxyProtocol:
trustedIPs: "10.0.0.0/16"
forwardedHeaders:
trustedIPs: "10.0.0.0/16"

View File

@@ -0,0 +1,18 @@
# SealedSecret created after namespace (sync-wave: 0)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: dot-ai-secrets
namespace: dot-ai
spec:
encryptedData:
anthropic-api-key: AgASIRVNs7kIvZ/XwFJ4j9TJ05TW4YlFNzi2lx+6URlvzjTkMignK+y4/HD3wTK37BkIonnOXf/DGJcMgqgxOrHBVu9tLkSamve5qgf+SOZ8jxaqpLX2e5hdmHrgMp7rVWKiOoxM+bvu8Gdve3eMwXX8Eazj7W7vi430LsvW4BmPrQp+A9SOP+hwMlV5r+P5pUogC3hddt5KoSwpgnpOb8fRGlLDpc548eDNBJrhsAZiiFqc8+CycHX6idxWzlWOTPR05LBs7YrTIP/LUXWdyq6UG8yB5D0c9JNndnGo6kSfJxdaxCNO7GhMAOmo4CitzYn/zhICwuHPLKwUYQBcEHVpFxgub0U9fY7s4i0SAH9aF1kk8yqHWaCicHzi6B3AxMZYr+knS5vVNB+uGZmSKgZ7QNE1bF6E4Gg5bXRwk7OVQhuJSBzxVNDoXoJ6stL8ejU+MP5FtI792FxZbNDImu3kqor/t9wB49ZeB9Ngks2yvtlSrQ3G2lxJaOgNBPmKYhAX95JFI3xUSHOMa18ftn+aXmEQTXOHtM/IT8A95gUvIKz8Hrt03CwgZ8E+3lcsjAVsfNjqn/erqV+xi93OYOL6o9ivmTIem3MzsqjKuQ57HKaghnd+Ygdt/WfotRBSgZtCweOkZBV0XKxI1RmCQXk6dce8x4T7FDEeaSQwkc4yZ77LfQJnyVgU1rds9Hqv0RbymHK8JCQEMMb6PcK+Ow068fdj4NP27HkuYMB5JdPp682KIRwGznN2d3+QPXRd/ZHshnixudY7CLirlZl9xD0HssfSBI+uCrFEAln8D93UYbn7trF5gkKWYQsQ07CVZ5Z1qXieqwJWjHRY+oY=
auth-token: AgB65rUY3yJuTTLbGyrCtJPZp8UyEOr+kznSMGvUaZ9PoHq+kIEhazRdVKHa/WGvMWuc4x+XYRuIGj+2KiooyilUPrLcE3bz9fUOAFbRw3+iUh3WAgwK+f3eBUG6/0OoFw+GJ583IRidDW1t2BchMxSM1m+3vmQoF24qj7k9j/lWPsnX2IX3DhM0SomD1xmG+LsQMo2e4vQXB0BxhVDIQ621JTrvUPYzYx0NlPqZO/MtR1JWYS7WBTvegvgeBKLxfy9KLnqsuzu7rc2t9BYt7TRqvBg/prrKPdSV6Ei4GOZq+AcG15iOKXhsj8SxejPpM0QDemFTRQkdfz+k8ms4/SM0eylr5fEaa99TMTvjYGCfjJJyRcVm5Ef/XdmXiM3OI1u+9QNPiqXh1zmtp0yQMZ7nRE70kKQ2MHVhEmSUkBjovybIzLk4OL1v0FGDDqa1BN9KmNEBlo8H7DqXzfamVoNPyuuO625B3HgSeR3Udq8hngy9W/wiolmMNSc9C6vBA0HzE7Mkq79fHh/ruTi5zOJLfyEP7KQQqlNKfEtDFQfabaV9ERthQjuUs8ZIrdfCTOyQkrmmGY/sH5yodLRSYSEpHFrhPepwHS7lGzbVucW3Fn34D4OxJXyXQQObZKybLV7g6Oglf2Pdn11CEu/4+19RlykuOlm1dlj852a5NWWmmX319laLGlEd1BpG4cZyc9UZud68dGumwzrUupUMTLlJx8bR5rLgqaMpQMluwvq8MQbBXm4ySOcQ2jQhjw==
openai-api-key: AgBc4cSrd0riSxKPrp7AqrNYMDAZJTe3aCjTOwEb5pS/KARkgoEGz+0ZhLXa2bcCsPlQzqRzZ1c1cUCnBACkp1xoMfhSOoIJUWJQHRgZx5kEDhNIE/M8PQe8es7jYsW7/ui6iK8uj3Ljk21r8l/ctnP/KiKyML4553el28Ya7XsPQynRvpVexFC4BoJw50nkauLnvl3in0cKmEVAkMWorUP3Etapaj4DWkCQQ22RPN0xGNo9yiIUEAVE+uTRFNKGEHMIvJzgB291FJQtG/WoN/Sy/DeoZkUYndA7CbfFgiz9sq3GqJwyqTM+Ikzw6VCQ3TVWYO/6+yaEzT1NZ/rw00YhDyFoH+yGTkX5lrSUo8lGf3T9OODTdSS1203xtj4dhGbY70sPGMxDfucnO6piVcuRfh9bWg1VX+MTjqpBQE1J0DKJanhoh6lKbPOJhRbi0TIoCz1a3btbbKLDq2YJl7FXA3QNdBsR4qVJ3xmgWlH/eTUskCE2YxDzHmkxgZN2mL22SfeUJYtDRswRG0UGc6pvg2xrR0iEDB0UbH4FQK46fWv8aiOejteLlAAyA9HNA8yi73rTEjq55wpO19/MYj+6oUGEHpFaJje77INTHAKpdbfkp8iKotNXFYLM2hsjyau+x/AFjg87kEdIUVVHHLVMDhOq6NO+foM0nFOIv4lr2Yjl15+ImMSTEfcBr+nWaomnb9G8i5ptqz9bMHbxAcHpzWUBH9SZhzrCWZNDzOm2X2K6mpXhg4WX7VJMoIJk/f+bxTTKnWBawdnpCdbdG5GYQh7usqjPuELFYRTsx+6Tqoddp4KIEyMMxO81XObiN5vEBg74ygunxrjKNg5FoOkUA79YvcGBVVU1FNRl3d8IslXFqhEOT35nxGBB0T7ZORii34tZ2E551NkIo1WbCwilD3Dlgw==
ui-auth-token: AgDStP4jHhMehx/SAv5x2aQrJwChnWq8WhV/LVTSuuCSSy9kFQGa3Izyl21sMhLKUgrlS030GH50lbku+IY90S+MSBfaCq0Xb8qwohfH+qJulMRgljoU8r58/3ZsaBzbGJjiNMgBCwGlvuK85t3R3662ftwikahWqLfwAahi12L0llUDNyG9UB0Os3oR2CVAt83+YB07YspCWRzwuOz9D1SgOL/g/JF2hoL43pDD65BqYKdEuLFInJZx1Ul68V/FPmJK1gmJb/Kv8pgtk0sGTzxbbTdIQ1Ugf6+8//CWEq6GsMeEZG9VwExL/KYqobnbqd02FRgSSvWybhWLjiALLQvAFtce6FR6bur0rsariyogGjYCuXYdRnhf7QnB6NopwplhFP37Qf6E4q2pJTJ6Fzv0xq+XlfzNkn9Jvebrdkk/5CThW+wPAfx8r1VIySqBsYnPO/ZQDkfQ5ocX6fmzZ1iCg+0NI24k2YwCeMZtEAGpKEtejS0BLvqrilUG6Oz9sHCGHro4Bv5B/jzUjGbye0DSDj1f6c2RcpqMiUxfPvydJIcJGhrTx8sZS3qhEWME7kwjJCZpHEzfdv0weSJbgVGBSh9e1wjZxxeJGXuZKdFzdhpNEWP4uScGW0UnRDwxZzHMsLjS/W30mQmwmgJVTKdPl6VQrvdq7m4AC6+/d7iXlHazieC1KpBme4hWzO+/h7qRw1P5Va/ZWZtnGs8476J8hIojRtOJ/CdWwVLUa0gHo4CYnempIMwCIS3GtA==
template:
metadata:
creationTimestamp: null
name: dot-ai-secrets
namespace: dot-ai