5 Commits

Author SHA1 Message Date
15b2fe1010 clusters 2026-04-18 19:29:59 +02:00
ae1c60cee3 multi-cluster 2026-04-18 19:26:51 +02:00
0d64249858 Merge branch 'main' of https://git.forteapps.net/Forte/launchpad into feature/multicluster 2026-04-18 16:09:13 +02:00
Danijel Simeunovic
ac0f464b2a fixes 2026-03-19 15:42:41 +01:00
Danijel Simeunovic
a681a9ae81 multi cluster 2026-03-18 22:28:38 +01:00
22 changed files with 199 additions and 283 deletions

View File

@@ -1,46 +0,0 @@
name: AI Code Review
on:
pull_request:
types: [ labeled, synchronize ]
jobs:
ai-review:
if: >-
(github.event.action == 'synchronized' && contains(toJSON(github.event.pull_request.labels), 'ai-review')) || contains(toJSON(gitea.event.changes.added_labels), '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 }}
# Review — disable fallback to see real Gitea API errors
REVIEW__INLINE_COMMENT_FALLBACK: "false"
# LLM configuration
LLM__PROVIDER: CLAUDE
LLM__META__MODEL: claude-sonnet-4-20250514
LLM__META__MAX_TOKENS: "4096"
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:v0.64.0
with:
args: ai-review run-inline
- name: Run summary review
uses: docker://nikitafilonov/ai-review:v0.64.0
with:
args: ai-review run-summary

34
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
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
View File

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

7
.project-standards.yaml Normal file
View File

@@ -0,0 +1,7 @@
standards_version: "2025.1"
last_configured: "2026-04-04"
components:
github-pages: "2025.1"
github-pages-generator: "mkdocs"
github-pages-source: "docs/"
github-pages-theme: "material"

View File

@@ -146,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-prod-values](ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git)** | App-specific configuration & versions | Developers / CI pipelines | ✅ Sometimes |
| **[helm-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-prod-values → ArgoCD syncs → Deployed to cluster
Developer commits code → CI/CD builds image → Updates helm-values → ArgoCD syncs → Deployed to cluster
```
**Learn more**: [GitOps Architecture - GitOps Workflow](docs/GITOPS-ARCHITECTURE.md#gitops-workflow)
@@ -166,7 +166,7 @@ Developer commits code → CI/CD builds image → Updates helm-prod-values → A
**Quick version**:
1. Create `apps/myapp.yaml` (ArgoCD Application manifest)
2. Create `helm-prod-values/myapp/values.yaml` (configuration)
2. Create `helm-values/myapp/values.yaml` (configuration)
3. Create sealed secrets if needed
4. Commit and push - ArgoCD auto-syncs!
@@ -175,8 +175,8 @@ Developer commits code → CI/CD builds image → Updates helm-prod-values → A
**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-prod-values
- **Update config**: Edit `helm-prod-values/myapp/values.yaml` → commit → push
- **Update code**: Push to app repo → CI/CD updates image tag in helm-values
- **Update config**: Edit `helm-values/myapp/values.yaml` → commit → push
### Manage Secrets
@@ -204,7 +204,7 @@ git push
**Quick version**:
```yaml
# In helm-prod-values/myapp/values.yaml
# In helm-values/myapp/values.yaml
# Token-based auth (simple)
auth:
@@ -366,7 +366,7 @@ kubectl patch application myapp -n argocd \
### Multi-Source Pattern
Applications reference both:
1. **Helm charts** from `forte-helm` (templates)
2. **Values** from `helm-prod-values` (configuration)
2. **Values** from `helm-values` (configuration)
This separates reusable templates from environment-specific config.
@@ -435,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-prod-values/`
3. Create Helm values in `helm-values/`
4. Create sealed secrets if needed
5. Commit and push - ArgoCD handles the rest!
@@ -485,8 +485,8 @@ Documentation lives in `docs/`. To update:
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
### Related Repositories
- [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
- [forte-helm](https://github.com/fortedigital/forte-helm) - Helm chart templates
- [helm-values](git@github.com:fortedigital/helm-values.git) - Application values
---

View File

@@ -4,4 +4,5 @@ resources:
- dot-ai-stack.yaml
- mcp10x.yaml
- musicman.yaml
- mcpcoder.yaml
- argo-mcp.yaml

View File

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

View File

@@ -2,7 +2,7 @@
# in case of $'\r': command not found error, run command below first
# sed -i 's/\r$//' ./bootstrap.sh
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (upc-dev|upc-prod)}"
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (eu|us)}"
echo "running $0 for cluster: ${CLUSTER}..."
@@ -17,18 +17,18 @@ echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..."
Bootstrap()
{
ArgoCd
# Gitea
# Github
}
############################################################
# Gitea #
# Github #
############################################################
Gitea()
Github()
{
echo "Installing secret..."
kubectl apply -f private/gitea-repo-main.yaml
kubectl apply -f private/main.key
kubectl apply -f private/github-${CLUSTER}.yaml
kubectl apply -f private/main-${CLUSTER}.key
}
############################################################

View File

@@ -1,10 +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
clusterName: dev-fd-us-east1
domain: us.forteapps.net
argocdDomain: argocd.us.forteapps.net
grafanaDomain: grafana.us.forteapps.net
keycloakDomain: id.us.forteapps.net
dotaiDomain: kubemcp.us.forteapps.net
dotaiUiDomain: kubemcpui.us.forteapps.net
letsencryptEmail: danijels@gmail.com
trustedIPs: "10.0.0.0/16"
cloudProvider: tbd

View File

@@ -96,10 +96,10 @@ You'll need read/write access to these repositories:
cd launchpad
```
2. **helm-prod-values** (Values repo)
2. **helm-values** (Values repo)
```bash
git clone https://git.forteapps.net/Forte/helm-prod-values.git
cd helm-prod-values
cd helm-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-prod-values repository with new tag │
│ - Updates helm-values repository with new tag │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Step 3: GitOps Sync (Automated) │
│ - ArgoCD detects change in helm-prod-values │
│ - ArgoCD detects change in helm-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-prod-values** | Application configuration (image tag, env vars) | Developers / CI pipelines | ✅ Sometimes |
| **helm-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-prod-values` (Your App Config)
#### Repository: `helm-values` (Your App Config)
```yaml
# myapp/values.yaml
# Your app's specific configuration
@@ -248,13 +248,13 @@ metadata:
namespace: argocd
spec:
sources:
- repoURL: https://git.forteapps.net/Forte/forte-helm
- repoURL: https://github.com/fortedigital/forte-helm
path: forteapp
helm:
valueFiles:
- $values/myapp/values.yaml
- repoURL: git@github.com:fortedigital/helm-prod-values.git
- repoURL: git@github.com:fortedigital/helm-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-prod-values
- name: Update helm-values
run: |
git clone git@github.com:fortedigital/helm-prod-values.git
cd helm-prod-values
git clone git@github.com:fortedigital/helm-values.git
cd helm-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-prod-values` repository:
Create a folder in `helm-values` repository:
```bash
cd ~/dev/k8s/helm-prod-values
@@ -412,7 +412,7 @@ spec:
sources:
# Source 1: Helm chart templates
- repoURL: https://git.forteapps.net/Forte/forte-helm
- repoURL: https://github.com/fortedigital/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-prod-values.git
- repoURL: git@github.com:fortedigital/helm-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-prod-values/myapp/values.yaml` with new tag
5. ✅ Updates `helm-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-prod-values/myapp/values.yaml`:
Update your `helm-values/myapp/values.yaml`:
```yaml
app:
@@ -791,7 +791,7 @@ Three authentication modes are supported:
#### Step 1: Configure Helm Values
```yaml
# In helm-prod-values/myapp/values.yaml
# In helm-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-prod-values/myapp/values.yaml
# In helm-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-prod-values/internal-api/values.yaml
# helm-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-prod-values/web-app/values.yaml
# helm-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-prod-values/mcp-server/values.yaml
# helm-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-prod-values/public-api/values.yaml
# helm-values/public-api/values.yaml
auth:
enabled: false # No authentication
@@ -1500,7 +1500,7 @@ kubectl exec -n myapp <pod-name> -- env
# Check if secrets exist
kubectl get secrets -n myapp
# Increase resources in helm-prod-values
# Increase resources in helm-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-prod-values` repository
- Keep configuration in `helm-values` repository
- Use environment variables for config
- Document what each value does
- Use reasonable resource limits

View File

@@ -47,7 +47,7 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
│ │ │
│ │ │
└────────► Update image tag ─┴──────────────────────────┘
in helm-prod-values │
in helm-values │
┌────────────────────────────────┐
@@ -184,7 +184,7 @@ launchpad/
---
### 2. **Helm Charts Repository**
**Repository**: `https://git.forteapps.net/Forte/forte-helm`
**Repository**: `https://github.com/fortedigital/forte-helm`
**Purpose**: Reusable Helm chart templates for Forte applications
**Location**: `C:\dev\k8s\forte-helm`
@@ -218,7 +218,7 @@ forte-helm/
---
### 3. **Helm Values Repository**
**Repository**: `git@github.com:fortedigital/helm-prod-values.git`
**Repository**: `git@github.com:fortedigital/helm-values.git`
**Purpose**: Environment-specific configuration for each application
**Location**: `C:\dev\k8s\helm-prod-values`
@@ -228,6 +228,8 @@ 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
```
@@ -277,7 +279,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-prod-values` repository
5. Update image tag in `helm-values` repository
6. ArgoCD detects change and syncs automatically
---
@@ -338,13 +340,13 @@ Applications like `mcp10x` and `musicman` use multiple sources:
```yaml
spec:
sources:
- repoURL: https://git.forteapps.net/Forte/forte-helm
- repoURL: https://github.com/fortedigital/forte-helm
path: forteapp # Helm chart templates
helm:
valueFiles:
- $values/mcp10x/values.yaml # Reference to second source
- repoURL: git@github.com:fortedigital/helm-prod-values.git
- repoURL: git@github.com:fortedigital/helm-values.git
targetRevision: HEAD
ref: values # Named reference
```
@@ -412,8 +414,8 @@ jobs:
- name: Update Helm values
run: |
git clone git@github.com:fortedigital/helm-prod-values.git
cd helm-prod-values/app
git clone git@github.com:fortedigital/helm-values.git
cd helm-values/app
sed -i "s/tag: .*/tag: $VERSION/" values.yaml
git commit -am "Update app to $VERSION"
git push
@@ -430,7 +432,7 @@ jobs:
- Syncs application to cluster
2. **Helm Values Change**:
- CI/CD updates `helm-prod-values/myapp/values.yaml`
- CI/CD updates `helm-values/myapp/values.yaml`
- ArgoCD detects change
- Pulls new Helm chart with updated values
- Applies to cluster
@@ -637,7 +639,7 @@ Notifications include:
✅ **DO**:
- Follow the `forteapp` chart pattern
- Use semantic versioning for image tags
- Update helm-prod-values via CI/CD
- Update helm-values via CI/CD
- Test locally with Docker Compose
- Document environment variables

View File

@@ -85,8 +85,7 @@ kubectl get applications -n argocd
1. **Configure DNS** for ingress domains:
- `argocd.127.0.0.1.nip.io` (local dev)
- `*.forteapps.net` (dev)
- `*.fortedigital.com` (production)
- `*.forteapps.net` (production)
2. **Verify Let's Encrypt certificates**:
```bash
@@ -108,7 +107,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 Gitea repositories.
ArgoCD needs SSH access to private Git repositories to pull manifests and Helm values. This section covers setting up deploy keys for GitHub repositories.
#### Why Deploy Keys?
@@ -120,7 +119,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 Gitea repository
- Write access to the GitHub repository
- ArgoCD installed and running
#### Setup Procedure
@@ -139,16 +138,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 Gitea)
- `argocd-deploy-key.pub` - Public key (add to GitHub)
**Step 2: Add Public Key to Gitea**
**Step 2: Add Public Key to GitHub**
1. Copy the public key:
```bash
cat argocd-deploy-key.pub
```
2. Go to Gitea repository settings:
2. Go to GitHub repository settings:
- Navigate to: `https://git.forteapps.net/Forte/launchpad/settings/keys`
- Or: Repository → Settings → Deploy keys
@@ -158,12 +157,12 @@ This creates two files:
- ☐ Allow write access (leave unchecked - read-only is sufficient)
- Click **"Add key"**
4. Repeat for the `helm-prod-values` repository if it's private:
4. Repeat for the `helm-values` repository if it's private:
```bash
# 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 ""
# Generate separate key for helm-values repo
ssh-keygen -t ed25519 -C "argocd-deploy-key-helm-values" -f argocd-helm-values-key -N ""
# Add to: https://git.forteapps.net/Forte/helm-prod-values/settings/keys
# Add to: https://github.com/fortedigital/helm-values/settings/keys
```
**Step 3: Create Kubernetes Secret**
@@ -271,7 +270,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 Gitea (keep old key for now)
# Add new public key to GitHub (keep old key for now)
# Update Kubernetes secret
kubectl create secret generic repo-launchpad \
@@ -279,7 +278,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 Gitea
# Test access, then remove old deploy key from GitHub
# Clean up
shred -u argocd-new-key
@@ -290,7 +289,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 Gitea
# Review deploy keys in GitHub
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
```
@@ -313,16 +312,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 Gitea
# Verify deploy key is added to GitHub
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
```
**Issue: "Host key verification failed"**
```bash
# Add Gitea to known_hosts
# Add GitHub to known_hosts
kubectl exec -n argocd deployment/argocd-repo-server -- \
ssh-keyscan git.forteapps.net >> ~/.ssh/known_hosts
ssh-keyscan github.com >> ~/.ssh/known_hosts
# Or disable strict host key checking (less secure)
kubectl patch secret repo-launchpad -n argocd \
@@ -347,16 +346,16 @@ kubectl rollout restart deployment argocd-application-controller -n argocd
#### Multiple Repository Setup
For the three-repository pattern (launchpad, forte-helm, helm-prod-values):
For the three-repository pattern (launchpad, forte-helm, helm-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-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
# 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
# 3. forte-helm (private helm charts repo)
@@ -367,14 +366,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-prod-values \
--from-file=sshPrivateKey=key-helm-prod-values \
kubectl create secret generic repo-helm-values \
--from-file=sshPrivateKey=key-helm-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-prod-values
shred -u key-sturdy key-helm-values
```
#### Converting HTTPS to SSH
@@ -391,7 +390,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://git.forteapps.net/Forte/|git@git.forteapps.net:Forte/|g' {} +
find . -name "*.yaml" -type f -exec sed -i 's|https://github.com/fortedigital/|git@github.com:fortedigital/|g' {} +
git add .
git commit -m "Switch from HTTPS to SSH for repository access"
@@ -495,7 +494,7 @@ spec:
See [Developer Guide](DEVELOPER-GUIDE.md#deploying-your-first-application) for detailed steps.
**Quick checklist:**
- [ ] Create `helm-prod-values/myapp/values.yaml`
- [ ] Create `helm-values/myapp/values.yaml`
- [ ] Create `apps/myapp.yaml` in config repo
- [ ] Create SealedSecret if needed
- [ ] Commit and push changes
@@ -560,7 +559,7 @@ kubectl scale deployment myapp -n myapp --replicas=3
#### GitOps Scaling
Update `helm-prod-values/myapp/values.yaml`:
Update `helm-values/myapp/values.yaml`:
```yaml
app:
@@ -574,7 +573,7 @@ Commit and push - ArgoCD will sync.
Enable Horizontal Pod Autoscaler:
```yaml
# In helm-prod-values/myapp/values.yaml
# In helm-values/myapp/values.yaml
app:
hpa:
enabled: true
@@ -623,7 +622,7 @@ kubectl rollout undo deployment myapp -n myapp
#### Option 3: Change Image Tag
```bash
# Edit helm-prod-values
# Edit helm-values
cd ~/dev/k8s/helm-prod-values
vim myapp/values.yaml
@@ -643,7 +642,7 @@ git push
#### Update Resource Limits
```yaml
# In helm-prod-values/myapp/values.yaml
# In helm-values/myapp/values.yaml
app:
resources:
requests:
@@ -657,7 +656,7 @@ app:
#### Enable Database
```yaml
# In helm-prod-values/myapp/values.yaml
# In helm-values/myapp/values.yaml
db:
enabled: true
persistence:
@@ -1267,7 +1266,7 @@ spec:
**What Needs Backup**:
- ❌ Cluster state (not backed up - recreate via GitOps)
- ❌ Persistent volumes (currently not critical)
- ✅ Git repositories (Gitea provides backup)
- ✅ Git repositories (GitHub provides backup)
- ⚠️ Secrets (sealed secrets in Git, unseal keys need safekeeping)
### Cluster Rebuild
@@ -1562,7 +1561,7 @@ git push
kubectl scale deployment myapp -n myapp --replicas=0
# Update Git
vim helm-prod-values/myapp/values.yaml
vim helm-values/myapp/values.yaml
# Set replicaCount: 0
git commit -am "Scale down myapp for maintenance"
git push
@@ -1635,7 +1634,7 @@ echo "Remember to delete: $SECRET_FILE"
- [ ] Application code repository created
- [ ] Dockerfile created and tested
- [ ] Gitea Actions workflow configured
- [ ] GitHub Actions workflow configured
- [ ] Helm values created in `helm-prod-values/`
- [ ] ArgoCD application manifest created in `apps/`
- [ ] Secrets created and sealed

View File

@@ -190,7 +190,7 @@ spec:
### Helm Charts Repository: `forte-helm`
**URL**: `https://git.forteapps.net/Forte/forte-helm`
**URL**: `https://github.com/fortedigital/forte-helm`
#### Chart: `forteapp`
@@ -337,18 +337,20 @@ configmap: [] # Application ConfigMap key-value pairs
---
### Helm Values Repository: `helm-prod-values`
### Helm Values Repository: `helm-values`
**URL**: `https://git.forteapps.net/Forte/helm-prod-values.git`
**URL**: `https://github.com/fortedigital/helm-values.git`
#### Structure
```
helm-prod-values/
helm-values/
├── mcp10x/
│ └── values.yaml
├── musicman/
│ └── values.yaml
├── mcpcoder/
│ └── values.yaml
└── argocd-mcp/
└── values.yaml
```
@@ -524,14 +526,14 @@ spec:
# Multi-source configuration
sources:
- repoURL: https://git.forteapps.net/Forte/forte-helm
- repoURL: https://github.com/fortedigital/forte-helm
path: forteapp
targetRevision: HEAD
helm:
valueFiles:
- $values/<app-name>/values.yaml
- repoURL: git@github.com:fortedigital/helm-prod-values.git
- repoURL: git@github.com:fortedigital/helm-values.git
targetRevision: HEAD
ref: values
@@ -815,21 +817,12 @@ 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.
**Auto-Watch**: Disabled (`AUTO_WATCH_ON_CHANGES: false`, `AUTO_WATCH_NEW_REPOS: false`). Prevents contributors from being auto-subscribed to repo notifications on push, reducing email noise from CI bots (e.g., ai-review PR comments). Users who were already watching before this change need to manually unwatch or switch to "Only participating".
**Endpoints**:
- Web UI: `https://git.forteapps.net`
- SSH: port 22 (ClusterIP)
- Metrics: `/metrics` (Prometheus scrape)
**Secrets**:
- `gitea-credentials` (SealedSecret) — admin password
- `gitea-oidc-credentials` (registrar-managed) — OIDC client ID + secret
- `gitea-smtp-secret` (SealedSecret) — SMTP username + password
**Secrets**: `gitea-credentials` (SealedSecret) containing `admin-password`, `postgres-password`, `secret` (OIDC client secret)
### Gitea Actions Runners
@@ -878,84 +871,6 @@ 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:repository` + `read:repository` scopes (use a bot/service account) |
**Setup Steps**:
1. Create a Gitea bot/service account and generate an API token with `write:repository` + `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`)

View File

@@ -17,7 +17,6 @@ resources:
- secrets.yaml
- gitea.yaml
- gitea-actions.yaml
- opencost.yaml
- renovate.yaml
- tempo.yaml
- grafana-dashboards.yaml

View File

@@ -29,10 +29,7 @@ gitea:
ALLOW_ONLY_EXTERNAL_REGISTRATION: true
ENABLE_BASIC_AUTHENTICATION: true
ENABLE_PASSWORD_SIGNIN_FORM: false
AUTO_WATCH_ON_CHANGES: false
AUTO_WATCH_NEW_REPOS: false
ENABLE_NOTIFY_MAIL: false
ENABLE_TIMETRACKING: false
ENABLE_NOTIFY_MAIL: true
openid:
ENABLE_OPENID_SIGNIN: false

View File

@@ -2,8 +2,6 @@ providers:
kubernetesIngress:
publishedService: # Fixes ArgoCD health checks for LoadBalancer services
enabled: true
kubernetesCRD:
allowCrossNamespace: true
deployment:
replicas: 2
@@ -50,26 +48,3 @@ ports:
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

@@ -10,10 +10,6 @@ service:
{
"name": "websecure",
"mode": "tcp"
},
{
"name": "gitea-ssh",
"mode": "tcp"
}
],
"backends": [
@@ -28,9 +24,6 @@ service:
"properties": {
"outbound_proxy_protocol": "v2"
}
},
{
"name": "gitea-ssh"
}
]
}

View File

@@ -1,5 +1,5 @@
global:
domain: argocd.fortedigital.com
domain: argocd.us.forteapps.net
notifications:
context:
clusterName: "prod-fd-no-svg1"
clusterName: "dev-fd-us-east1"

View File

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

View File

@@ -1,3 +1,3 @@
ingress:
hosts:
- grafana.fortedigital.com
- grafana.us.forteapps.net

View File

@@ -1,2 +1,2 @@
ingress:
hostname: id.fortedigital.com
hostname: id.us.forteapps.net

43
mkdocs.yml Normal file
View File

@@ -0,0 +1,43 @@
site_name: K8s Launchpad
site_description: Documentation for the GitOps-managed Kubernetes cluster
repo_url: https://git.forteapps.net/Forte/launchpad
repo_name: Forte/launchpad
theme:
name: material
palette:
- scheme: default
primary: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.instant
- navigation.sections
- navigation.top
- search.highlight
- content.code.copy
nav:
- Home: README.md
- GitOps Architecture: GITOPS-ARCHITECTURE.md
- Developer Guide: DEVELOPER-GUIDE.md
- Operations Runbook: OPERATIONS-RUNBOOK.md
- Technical Reference: REFERENCE.md
markdown_extensions:
- tables
- toc:
permalink: true
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- admonition
- pymdownx.details