Complete documentation for future sessions

- CLAUDE.md for AI agents to understand the codebase
- GITEA-GUIDE.md centralizes all Gitea operations (API, Registry, Auth)
- DEVELOPMENT-WORKFLOW.md explains complete dev process
- ROADMAP.md, NEXT-SESSION.md for planning
- QUICK-REFERENCE.md, TROUBLESHOOTING.md for daily use
- 40+ detailed docs in /docs folder
- Backend as submodule from Gitea

Everything documented for autonomous operation.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hector Ros
2026-01-20 00:36:53 +01:00
commit db71705842
49 changed files with 19162 additions and 0 deletions

View File

@@ -0,0 +1,474 @@
# Networking e Ingress
## Arquitectura de Red
```
Internet
[LoadBalancer] (Cloud Provider)
[Nginx Ingress Controller]
├──► api.aiworker.dev ──► Backend (control-plane)
├──► git.aiworker.dev ──► Gitea (gitea)
├──► app.aiworker.dev ──► Frontend (control-plane)
├──► *.preview.aiworker.dev ──► Preview Deployments
├──► staging-*.aiworker.dev ──► Staging Envs
└──► *.aiworker.dev ──► Production Apps
```
## Ingress Configuration
### Wildcard Certificate
```yaml
# k8s/ingress/wildcard-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-aiworker
namespace: ingress-nginx
spec:
secretName: wildcard-aiworker-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: "*.aiworker.dev"
dnsNames:
- "aiworker.dev"
- "*.aiworker.dev"
- "*.preview.aiworker.dev"
```
### Backend Ingress
```yaml
# k8s/ingress/backend-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-ingress
namespace: control-plane
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/websocket-services: "aiworker-backend"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.aiworker.dev"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, PATCH, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.aiworker.dev
secretName: backend-tls
rules:
- host: api.aiworker.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: aiworker-backend
port:
number: 3000
```
### Frontend Ingress
```yaml
# k8s/ingress/frontend-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
namespace: control-plane
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
spec:
ingressClassName: nginx
tls:
- hosts:
- app.aiworker.dev
secretName: frontend-tls
rules:
- host: app.aiworker.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: aiworker-frontend
port:
number: 80
```
### Preview Deployments Ingress Template
```typescript
// services/kubernetes/ingress.ts
export function generatePreviewIngress(params: {
taskId: string
projectName: string
namespace: string
}) {
const shortId = params.taskId.slice(0, 8)
const host = `task-${shortId}.preview.aiworker.dev`
return {
apiVersion: 'networking.k8s.io/v1',
kind: 'Ingress',
metadata: {
name: `${params.projectName}-preview`,
namespace: params.namespace,
annotations: {
'cert-manager.io/cluster-issuer': 'letsencrypt-prod',
'nginx.ingress.kubernetes.io/ssl-redirect': 'true',
'nginx.ingress.kubernetes.io/auth-type': 'basic',
'nginx.ingress.kubernetes.io/auth-secret': 'preview-basic-auth',
'nginx.ingress.kubernetes.io/auth-realm': 'Preview Environment',
},
labels: {
environment: 'preview',
task: params.taskId,
project: params.projectName,
},
},
spec: {
ingressClassName: 'nginx',
tls: [
{
hosts: [host],
secretName: `${params.projectName}-preview-tls`,
},
],
rules: [
{
host,
http: {
paths: [
{
path: '/',
pathType: 'Prefix',
backend: {
service: {
name: `${params.projectName}-preview`,
port: {
number: 80,
},
},
},
},
],
},
},
],
},
}
}
```
## Service Mesh (Opcional)
Si necesitas más control sobre el tráfico, considera usar Istio o Linkerd:
### Istio Gateway
```yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: aiworker-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: wildcard-aiworker-tls
hosts:
- "*.aiworker.dev"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: backend-vs
namespace: control-plane
spec:
hosts:
- "api.aiworker.dev"
gateways:
- istio-system/aiworker-gateway
http:
- match:
- uri:
prefix: /api
route:
- destination:
host: aiworker-backend
port:
number: 3000
```
## DNS Configuration
### Cloudflare DNS Records
```bash
# A records
api.aiworker.dev A <loadbalancer-ip>
git.aiworker.dev A <loadbalancer-ip>
app.aiworker.dev A <loadbalancer-ip>
# Wildcard for preview and dynamic environments
*.preview.aiworker.dev A <loadbalancer-ip>
*.aiworker.dev A <loadbalancer-ip>
```
### External DNS (Automated)
```yaml
# k8s/external-dns/external-dns-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.14.0
args:
- --source=ingress
- --domain-filter=aiworker.dev
- --provider=cloudflare
env:
- name: CF_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-api-token
key: token
```
## Network Policies
### Isolate Preview Environments
```yaml
# k8s/network-policies/preview-isolation.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: preview-isolation
namespace: agents
spec:
podSelector:
matchLabels:
environment: preview
policyTypes:
- Ingress
- Egress
ingress:
# Allow from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
# Allow from control-plane
- from:
- namespaceSelector:
matchLabels:
name: control-plane
egress:
# Allow to gitea
- to:
- namespaceSelector:
matchLabels:
name: gitea
# Allow to external HTTPS (npm, apt, etc)
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
```
### Allow Backend to All
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress
namespace: control-plane
spec:
podSelector:
matchLabels:
app: aiworker-backend
policyTypes:
- Egress
egress:
- {} # Allow all egress
```
## Load Balancing
### Session Affinity for WebSocket
```yaml
apiVersion: v1
kind: Service
metadata:
name: aiworker-backend
namespace: control-plane
annotations:
service.beta.kubernetes.io/external-traffic: OnlyLocal
spec:
selector:
app: aiworker-backend
ports:
- name: http
port: 3000
targetPort: 3000
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600
type: ClusterIP
```
## Rate Limiting
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-ingress
namespace: control-plane
annotations:
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-burst: "200"
nginx.ingress.kubernetes.io/rate-limit-key: "$binary_remote_addr"
spec:
# ... spec
```
## Health Checks
### Liveness and Readiness Probes
```yaml
livenessProbe:
httpGet:
path: /api/health
port: 3000
httpHeaders:
- name: X-Health-Check
value: liveness
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/health/ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
```
### Health Endpoint Implementation
```typescript
// api/routes/health.ts
import { Router } from 'express'
import { getDatabase } from '../../config/database'
import { getRedis } from '../../config/redis'
const router = Router()
router.get('/health', async (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
})
})
router.get('/health/ready', async (req, res) => {
try {
// Check DB
const db = getDatabase()
await db.execute('SELECT 1')
// Check Redis
const redis = getRedis()
await redis.ping()
res.json({
status: 'ready',
services: {
database: 'connected',
redis: 'connected',
},
})
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message,
})
}
})
export default router
```
## Monitoring Traffic
```bash
# Ver logs de Nginx Ingress
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller --tail=100 -f
# Ver métricas
kubectl top pods -n ingress-nginx
# Ver configuración generada
kubectl exec -n ingress-nginx <pod> -- cat /etc/nginx/nginx.conf
```