39 Commits

Author SHA1 Message Date
ecbb1f8638 pw 2026-04-23 23:00:44 +02:00
424be7ec7e allow login and sync 2026-04-23 22:49:53 +02:00
5afdf00964 session 2026-04-23 21:54:24 +02:00
2781c96d43 tls 2026-04-23 21:50:38 +02:00
a456a11460 db 2026-04-23 21:40:55 +02:00
b5e442d92b policy 2026-04-23 21:25:11 +02:00
2d756295bf backstage resources 2026-04-23 20:52:38 +02:00
026bcb2b31 feature/backstage (#13)
Reviewed-on: #13
Reviewed-by: gitea_admin <admin@forteapps.net>
Co-authored-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
Co-committed-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
2026-04-23 18:45:57 +00:00
aa6775bed2 ns 2026-04-23 14:52:27 +02:00
06522b2f19 ts-mcp 2026-04-23 14:44:33 +02:00
4c65035485 ns 2026-04-23 14:11:45 +02:00
84f4bebc08 ts-mcp 2026-04-23 13:41:51 +02:00
5394b2c714 ts-mcp 2026-04-23 13:40:33 +02:00
c4e586a7be ts-mcp 2026-04-23 13:38:47 +02:00
1fa070b041 argo 2026-04-23 13:35:42 +02:00
9c905355e3 argocd known host 2026-04-23 13:28:34 +02:00
6b1115ec28 argocd disable submodule 2026-04-23 13:09:02 +02:00
2fb276a62c ts-mcp 2026-04-23 13:02:00 +02:00
3efe1b68ef auth doc 2026-04-23 10:05:15 +02:00
5df104beec sp 2026-04-22 13:54:51 +02:00
0ecfee3cf8 prompts 2026-04-22 13:51:38 +02:00
c88938adb5 feature/ai-review (#7)
Co-authored-by: gitea_admin <admin@forteapps.net>
Reviewed-on: #7
Co-authored-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
Co-committed-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
2026-04-22 09:30:02 +00:00
d05a16840e pr trigger 2026-04-22 09:11:40 +02:00
d7c7242aa1 submodule 2026-04-22 09:10:38 +02:00
3bf9fa7837 pr label 2026-04-22 08:48:05 +02:00
d2596568f2 version tag 2026-04-21 15:17:52 +02:00
2a3539350b AI-review (#6)
Co-authored-by: gitea_admin <admin@forteapps.net>
Reviewed-on: #6
Co-authored-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
Co-committed-by: Danijel Simeunovic <danijel.simeunovic@fortedigital.com>
2026-04-21 08:20:41 +00:00
f97b613c12 remove unneeded yml 2026-04-20 22:46:44 +02:00
9c7db11470 remove unneeded yml 2026-04-20 22:45:53 +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
31 changed files with 744 additions and 248 deletions

View File

@@ -0,0 +1,47 @@
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
token: ${{ secrets.AI_REVIEW_TOKEN }}
- 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

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

@@ -1,7 +0,0 @@
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-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)
@@ -166,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!
@@ -175,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
@@ -204,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:
@@ -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-values` (configuration)
2. **Values** from `helm-prod-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-values/`
3. Create Helm values in `helm-prod-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://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

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

50
apps/base/ts-mcp.yaml Normal file
View File

@@ -0,0 +1,50 @@
---
# Namespace must be created first (sync-wave: -1)
apiVersion: v1
kind: Namespace
metadata:
name: ts-mcp
annotations:
argocd.argoproj.io/sync-wave: "-1"
---
# ArgoCD Application syncs last (sync-wave: 11)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ts-mcp
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "11"
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: ""
notifications.argoproj.io/subscribe.on-sync-failed.slack: ""
notifications.argoproj.io/subscribe.on-degraded.slack: ""
labels:
app.kubernetes.io/name: ts-mcp
app.kubernetes.io/part-of: apps
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
path: forteapp
targetRevision: HEAD
helm:
valueFiles:
- $values/ts-mcp/values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: ts-mcp
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

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> (eu|us)}"
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (upc-dev|upc-prod)}"
echo "running $0 for cluster: ${CLUSTER}..."
@@ -17,18 +17,18 @@ echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..."
Bootstrap()
{
ArgoCd
# Github
# Gitea
}
############################################################
# Github #
# Gitea #
############################################################
Github()
Gitea()
{
echo "Installing secret..."
kubectl apply -f private/github-${CLUSTER}.yaml
kubectl apply -f private/main-${CLUSTER}.key
kubectl apply -f private/gitea-repo-main.yaml
kubectl apply -f private/main.key
}
############################################################

View File

@@ -0,0 +1,43 @@
# Self-service Keycloak client config for Backstage.
# Kyverno clones this to the keycloak namespace, where the
# keycloak-client-registrar CronJob processes it and creates
# the backstage-oidc-credentials Secret in the backstage namespace.
apiVersion: v1
kind: Secret
metadata:
name: keycloak-client-backstage
namespace: backstage
labels:
keycloak.forteapps.net/client-config: "true"
stringData:
client.json: |
{
"clientId": "backstage",
"name": "Backstage Developer Portal",
"redirectUris": ["https://backstage.forteapps.net/api/auth/oidc/handler/frame"],
"webOrigins": ["https://backstage.forteapps.net"],
"defaultClientScopes": ["openid", "email", "profile"],
"protocolMappers": [
{
"name": "email_verified",
"protocol": "openid-connect",
"protocolMapper": "oidc-hardcoded-claim-mapper",
"config": {
"claim.name": "email_verified",
"claim.value": "true",
"jsonType.label": "boolean",
"id.token.claim": "true",
"access.token.claim": "true",
"userinfo.token.claim": "true"
}
}
],
"secret": {
"namespace": "backstage",
"name": "backstage-oidc-credentials",
"keys": {
"clientId": "AUTH_OIDC_CLIENT_ID",
"clientSecret": "AUTH_OIDC_CLIENT_SECRET"
}
}
}

View File

@@ -1,41 +0,0 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
annotations:
policies.kyverno.io/title: Require Labels
policies.kyverno.io/category: Best Practices
policies.kyverno.io/minversion: 1.6.0
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod, Label
policies.kyverno.io/description: Define and use labels that identify semantic attributes of your application or Deployment. A common set of labels allows tools to work collaboratively, describing objects in a common manner that all tools can understand. The recommended labels describe applications in a way that can be queried. This policy validates that the label `app.kubernetes.io/name` is specified with some value.
spec:
validationFailureAction: Audit
background: true
rules:
- name: check-for-labels
skipBackgroundRequests: true
exclude:
any:
- resources:
namespaces:
- kube-system
- istio-system
- argocd
- cert-manager
- monitoring
- secrets
- kyverno
- trivy-system
match:
any:
- resources:
kinds:
- Pod
validate:
message: The label `app.kubernetes.io/name` is required.
allowExistingViolations: true
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"

View File

@@ -1,10 +1,10 @@
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
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
@@ -962,6 +962,46 @@ User sees application (authenticated)
---
### Accessing Authenticated User Information
The auth sidecar handles all authentication before requests reach your application. Your app never sees unauthenticated traffic — the sidecar returns 401 or redirects to the IdP first.
After successful authentication, the sidecar forwards the request to your application with user identity injected as HTTP headers:
| Header | Description | Available in |
|--------|-------------|-------------|
| `X-Auth-User` | Username or display name | Token, OIDC, MCP |
| `X-Auth-Email` | User email address | OIDC |
| `X-Auth-Subject` | OIDC `sub` claim (stable user ID) | OIDC, MCP |
| `X-Auth-Groups` | Comma-separated group memberships | OIDC (if scope includes `groups`) |
| `X-Auth-Token` | The validated access token | All modes |
**Your application reads these headers — no auth library needed:**
```javascript
// Express.js example
app.get('/profile', (req, res) => {
const user = req.headers['x-auth-user'];
const email = req.headers['x-auth-email'];
res.json({ user, email });
});
```
```python
# Flask example
@app.route('/profile')
def profile():
user = request.headers.get('X-Auth-User')
email = request.headers.get('X-Auth-Email')
return jsonify(user=user, email=email)
```
**Why this is safe**: The Kyverno-generated NetworkPolicy restricts ingress to the sidecar port only. Traffic cannot bypass the sidecar to reach the application port directly, so the `X-Auth-*` headers can be trusted unconditionally.
**Key principle**: Your application is zero-trust-unaware by design. It reads headers and renders UI. All authentication complexity lives in the sidecar and Kyverno policy.
---
### Authentication Configuration Reference
#### Helm Values Schema
@@ -1049,7 +1089,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 +1117,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 +1152,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 +1176,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
@@ -1500,7 +1540,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 +1689,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

@@ -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 │
┌────────────────────────────────┐
@@ -184,7 +184,7 @@ launchpad/
---
### 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`
@@ -218,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`
@@ -228,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
```
@@ -279,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
---
@@ -340,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
```
@@ -414,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
@@ -432,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
@@ -639,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**
@@ -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
@@ -1561,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
@@ -1634,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

@@ -190,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`
@@ -337,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
```
@@ -526,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
@@ -604,6 +602,15 @@ retry:
4. 40 seconds
5. 80 seconds (capped at 3 minutes)
### Global Settings (`argocd-cm`)
| Setting | Value | Purpose |
|---------|-------|---------|
| `application.resourceTrackingMethod` | `annotation` | Track resources via annotations |
| `timeout.reconciliation` | `60s` | Reconciliation interval |
| `admin.enabled` | `true` | Enable admin account |
| `git.submodule.enabled` | `false` | Disable git submodule checkout — submodules are not needed for manifest generation |
---
## Infrastructure Components
@@ -817,12 +824,21 @@ 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) 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
@@ -871,6 +887,161 @@ 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
### Backstage / RHDH (Developer Portal)
**Chart**: `backstage` (RHDH — Red Hat Developer Hub)
**Version**: `5.8.0`
**Namespace**: `backstage`
**Helm Repo**: `https://redhat-developer.github.io/rhdh-chart`
**Image**: `quay.io/rhdh-community/rhdh:next`
**Purpose**: Internal developer portal where teams register and broadcast themselves, their applications, APIs, and systems. Provides a unified catalog, templates, and documentation hub.
**Why RHDH over vanilla Backstage**: Ships 27+ plugins pre-bundled (ArgoCD, Kubernetes, Keycloak, GitHub, GitLab, Jira, SonarQube, Tekton, Jenkins, Quay, and more). Supports dynamic plugin installation at runtime — no image rebuilds needed.
**Configuration** (`infra/values/base/backstage-values.yaml`):
- OpenShift Route disabled (`route.enabled: false`) — uses Traefik ingress instead
- PostgreSQL subchart enabled for persistence (2Gi)
- SecurityContext configured for vanilla Kubernetes (non-OpenShift)
- Traefik ingress with `websecure` entrypoint
- App title: "Forte Developer Portal"
- Dynamic plugins: loads `dynamic-plugins.default.yaml` (all 27+ bundled plugins)
- Catalog rules: Component, System, API, Resource, Location, Template, Group, User, Domain
**Authentication** (Keycloak OIDC):
- Uses the self-service registrar pattern (see [Keycloak Client Registrar](#keycloak-client-registrar))
- Config Secret: `cluster-resources/backstage-keycloak-client-config.yaml`
- Kyverno clones it → registrar creates `backstage-oidc-credentials` Secret in `backstage` namespace
- Credential keys: `AUTH_OIDC_CLIENT_ID`, `AUTH_OIDC_CLIENT_SECRET` (loaded via `extraEnvVarsSecrets`)
- Redirect URI: `https://backstage.forteapps.net/api/auth/oidc/handler/frame`
- Sign-in resolver: `emailMatchingUserEntityProfileEmail`
**Catalog Discovery** (Gitea):
- Auto-discovers `catalog-info.yaml` from all repos in the `Forte` organization
- Scans every 30 minutes via the Gitea catalog provider plugin
- Gitea SCM integration configured for URL resolution (`git.forteapps.net`)
**Catalog Registration**:
Teams register services by adding a `catalog-info.yaml` to their repo root:
```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-service
description: My service description
annotations:
backstage.io/source-location: url:https://git.forteapps.net/Forte/my-service
spec:
type: service
lifecycle: production
owner: team-name
```
Repos with this file are auto-discovered — no manual registration needed.
**Dynamic Plugins**:
Add plugins at runtime via `global.dynamic.plugins` in values — no image rebuild:
```yaml
global:
dynamic:
plugins:
- package: "@scope/my-plugin@1.0.0"
integrity: "sha512-..."
```
**Per-cluster Configuration** (`infra/values/upc-dev/backstage-values.yaml`):
```yaml
global:
host: backstage.forteapps.net
upstream:
backstage:
appConfig:
app:
baseUrl: https://backstage.forteapps.net
backend:
baseUrl: https://backstage.forteapps.net
ingress:
host: backstage.forteapps.net
```
### Keycloak Client Registrar
**Type**: CronJob (deployed via Keycloak Helm chart `extraDeploy`)
@@ -1431,7 +1602,23 @@ Forward to Application (localhost:3000)
Application processes request
```
**See**: [Developer Guide - Enabling Authentication](DEVELOPER-GUIDE.md#enabling-authentication-for-applications) for usage examples.
#### Forwarded Headers
After successful authentication, the sidecar injects user identity as HTTP headers before forwarding the request to the application container:
| Header | Description | Auth Modes |
|--------|-------------|------------|
| `X-Auth-User` | Username or display name | Token, OIDC, MCP |
| `X-Auth-Email` | User email address | OIDC |
| `X-Auth-Subject` | OIDC `sub` claim (stable user ID) | OIDC, MCP |
| `X-Auth-Groups` | Comma-separated group memberships | OIDC (if `groups` scope) |
| `X-Auth-Token` | The validated access token | All modes |
These headers are trustworthy because the auto-generated `NetworkPolicy` restricts pod ingress to the sidecar port only — external traffic cannot reach the application container directly, so headers cannot be spoofed.
Applications should read these headers to obtain authenticated user information (e.g. for display, authorisation decisions, or audit logging) instead of implementing their own authentication.
**See**: [Developer Guide - Accessing Authenticated User Information](DEVELOPER-GUIDE.md#accessing-authenticated-user-information) for code examples.
---

43
infra/base/backstage.yaml Normal file
View File

@@ -0,0 +1,43 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: backstage
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/name: backstage
app.kubernetes.io/part-of: developer-portal
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: https://redhat-developer.github.io/rhdh-chart
chart: backstage
targetRevision: "5.8.0"
helm:
releaseName: backstage
valueFiles:
- $values/infra/values/base/backstage-values.yaml
- $values/infra/values/upc-dev/backstage-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: backstage
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true

View File

@@ -17,7 +17,9 @@ resources:
- secrets.yaml
- gitea.yaml
- gitea-actions.yaml
- opencost.yaml
- renovate.yaml
- tempo.yaml
- grafana-dashboards.yaml
- network-policies-application.yaml
- backstage.yaml

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

@@ -2,12 +2,21 @@ configs:
secret:
createSecret: true
argocdServerAdminPassword: "$2b$12$Tmb1jH7ADvwWoUoNPXXsfOf6JqEluqhq8mL06a8DGT2AP1GzbNsCm"
ssh:
knownHosts: |
[git.forteapps.net]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDTwi40de8yTGUuRT0i/XGicQ672BLhYR6D/lDquJrp/tdrWoZhVVPy0wxSkWsq1V92iiAUuQnXagOGsLBGZT9uDLWKvEmNDnCfjzTMq3J1iA3vk2rQ8WBlCzhvmeCV/r0ufl6vsgfwxSRomLZeqa2UkLHx69gy2Njb1S2/aZK1Q53f466hCUfDULZrTn2Nn5Sj8cEbJ8EyvVN2YG9HYBxQdzKRPZEmS1vyzmn8YrYIkZseIRQElabzWGh86owuaaqnwJhTJj1j2sEUeIet04sGKJcnxx2UL4H90N66LKMldmMiuli+ve/CjJmMwDl0zGkjIniT3XR8CyEXYHli7B1hR8Z+dbK6DBgjz+28lFgMIRY70KkZJNsJcBNZLZ5fHwCI13a9U3Uhg3Pu/6s0zlosM4CrAQNQCRe95ZPtCpdFhlGrOl4m1rdSK2meL6rND0TBBuZbaFF6Py7TawLCAiO2KRaVqhu9OFVjwJ/nifgLzFGwWj+WcYmpuR+DwozrF/Hl7QYsz1x4GO1SONY07KbIFkUCHOMAh0AELY5YE4eGI4mtG6SecdPaAdLREGZYK4IcyP5i1QW9g0wmfRSsV9jy+r0ivBxixxh4yJiNpkg6NXak40gQtGIme9EJ+DxrRLruNsfDILWcdSuH/wvuorv56NpQFGB0FzB6LXMloSYptQ==
cm:
application.resourceTrackingMethod: annotation
timeout.reconciliation: 60s
admin.enabled: "true"
params:
"server.insecure": true
repoServer:
env:
# Disable git submodule checkout - submodules (e.g. shared-prompts)
# are not needed for K8s manifest generation
- name: ARGOCD_GIT_MODULES_ENABLED
value: "false"
server:
ingress:
enabled: false

View File

@@ -0,0 +1,150 @@
# Red Hat Developer Hub (RHDH) - Internal Developer Portal
# Helm chart: https://github.com/redhat-developer/rhdh-chart
# Includes 27+ plugins out of the box: ArgoCD, Kubernetes, Keycloak,
# GitHub, GitLab, Jira, SonarQube, Tekton, Jenkins, and more.
global:
auth:
backend:
enabled: true
dynamic:
includes:
- dynamic-plugins.default.yaml
plugins: []
# Disable OpenShift Route (not on OpenShift)
route:
enabled: false
upstream:
backstage:
image:
registry: quay.io
repository: rhdh-community/rhdh
tag: next
podSecurityContext:
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
extraEnvVarsSecrets:
- backstage-oidc-credentials
- backstage-session-secret
appConfig:
app:
title: "Forte Backstage"
baseUrl: http://localhost:7007
backend:
baseUrl: http://localhost:7007
# -- Keycloak OIDC authentication
signInPage: oidc
auth:
session:
secret: ${AUTH_SESSION_SECRET}
environment: production
providers:
oidc:
production:
metadataUrl: https://id.forteapps.net/realms/forte/.well-known/openid-configuration
clientId: ${AUTH_OIDC_CLIENT_ID}
clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
prompt: auto
# Allow login before User entities exist in the catalog.
# Remove once org data is populated.
dangerouslyAllowSignInWithoutUserInCatalog: true
signIn:
resolvers:
- resolver: emailMatchingUserEntityProfileEmail
# -- Gitea SCM integration (for catalog URL resolution)
integrations:
gitea:
- host: git.forteapps.net
# -- Software catalog
catalog:
rules:
- allow:
- Component
- System
- API
- Resource
- Location
- Template
- Group
- User
- Domain
providers:
# Auto-import users and groups from Keycloak
keycloakOrg:
default:
baseUrl: https://id.forteapps.net
realm: forte
clientId: ${AUTH_OIDC_CLIENT_ID}
clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
initialDelay: { seconds: 15 }
# Auto-discover catalog-info.yaml from all Forte org repos
gitea:
forte:
organization: Forte
host: git.forteapps.net
catalogPath: catalog-info.yaml
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
locations:
# Backstage's own org data (bootstrap teams, systems, domains)
# - type: url
# target: https://git.forteapps.net/Forte/backstage-catalog/raw/branch/main/org.yaml
# rules:
# - allow: [Group, User, System, Domain]
ingress:
enabled: true
className: traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
enabled: true
secretName: backstage-tls
postgresql:
enabled: true
auth:
# Fixed passwords prevent Helm from regenerating the Secret on
# each sync, which would mismatch with the PVC-persisted data.
password: backstage-db-pw
postgresPassword: backstage-admin-pw
primary:
persistence:
enabled: true
size: 2Gi
podSecurityContext:
enabled: true
fsGroup: 26
runAsUser: 26
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 250m
memory: 512Mi
volumePermissions:
enabled: false

View File

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

View File

@@ -116,12 +116,12 @@ extraDeploy:
metadata:
name: keycloak-client-registrar
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list"]
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "get", "list", "create", "update", "patch" ]
- apiGroups: [ "" ]
resources: [ "namespaces" ]
verbs: [ "get", "list" ]
# -- ClusterRoleBinding for the registrar ServiceAccount
- apiVersion: rbac.authorization.k8s.io/v1
@@ -158,7 +158,7 @@ extraDeploy:
containers:
- name: registrar
image: alpine:3.20
command: ["/bin/sh", "-c"]
command: [ "/bin/sh", "-c" ]
args:
- |
set -e

View File

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

@@ -0,0 +1,12 @@
global:
host: backstage.forteapps.net
upstream:
backstage:
appConfig:
app:
baseUrl: https://backstage.forteapps.net
backend:
baseUrl: https://backstage.forteapps.net
ingress:
host: backstage.forteapps.net

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,43 +0,0 @@
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

1
shared-prompts Submodule

Submodule shared-prompts added at c5bc55b3d7