add neuron-web phase-1 deploy on Legion
Stand up the elb-built native El landing server on Legion k3s alongside the existing Cloud Run prod marketing site. Image is built on gitea-runner-1 from elb-emitted .c files + native cc and pushed to registry.neuralplatform.ai/neuron-web:dev-dfe41234. Deployment exposes web-stage.neuralplatform.ai via Traefik + Cloudflare tunnel. The point is to have a working local instance we can iterate against while we redesign the CI/CD pipeline; nothing here replaces the Cloud Run deploy yet. * k8s/neuron-technologies/web/ — namespace, deployment, service, ingress, registry-pull-secret + externalsecret pulling anthropic/supabase keys from Vault * apps/neuron-web.yaml — Argo CD Application * dns-neuralplatform.tf, main.tf — CNAME + tunnel ingress for web-stage.neuralplatform.ai
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# neuron-web — El landing page server (Phase 1 deploy on Legion)
|
||||
#
|
||||
# Sibling to neuron-marketing (Next.js prod) and neuron-stage (Next.js stage).
|
||||
# This deployment runs the elb-built native El binary at
|
||||
# web-stage.neuralplatform.ai. Coexists with the Cloud Run prod marketing
|
||||
# site; nothing here replaces that yet.
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: neuron-web
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: http://gitea.git.svc.cluster.local:3000/will/infrastructure.git
|
||||
targetRevision: main
|
||||
path: servers/legion/k8s/neuron-technologies/web
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: neuron-web
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
@@ -11,3 +11,15 @@ resource "cloudflare_record" "np_vpn" {
|
||||
proxied = true
|
||||
ttl = 1
|
||||
}
|
||||
|
||||
# web-stage.neuralplatform.ai — Phase 1 Legion deploy of neuron-web (the El
|
||||
# native landing server). Coexists with the Cloud Run prod marketing site
|
||||
# at neurontechnologies.ai; this lets us iterate on the native build.
|
||||
resource "cloudflare_record" "np_web_stage" {
|
||||
zone_id = local.zone_neuralplatform_ai
|
||||
name = "web-stage"
|
||||
type = "CNAME"
|
||||
content = "${var.cloudflare_tunnel_id}.cfargotunnel.com"
|
||||
proxied = true
|
||||
ttl = 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: neuron-web
|
||||
namespace: neuron-web
|
||||
labels:
|
||||
app: neuron-web
|
||||
env: stage
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: neuron-web
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: neuron-web
|
||||
env: stage
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: registry-pull-secret
|
||||
containers:
|
||||
- name: neuron-web
|
||||
image: registry.neuralplatform.ai/neuron-web:dev-dfe41234
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: production
|
||||
- name: PORT
|
||||
value: "8080"
|
||||
- name: NEURON_PORT
|
||||
value: "7772"
|
||||
- name: LANDING_ROOT
|
||||
value: /srv/landing
|
||||
# Public Cloud Run env that the El runtime reads at startup.
|
||||
- name: NEURON_LLM_0_FORMAT
|
||||
value: anthropic
|
||||
- name: NEURON_LLM_0_MODEL
|
||||
value: claude-sonnet-4-5
|
||||
- name: NEURON_LLM_0_URL
|
||||
value: https://api.anthropic.com/v1/messages
|
||||
- name: GCS_SHARE_BUCKET
|
||||
value: neuron-shares-prod
|
||||
- name: NEURON_ORIGIN
|
||||
value: https://web-stage.neuralplatform.ai
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: neuron-web-secrets
|
||||
# The El runtime fs_writes index.html, about.html, terms.html,
|
||||
# enterprise-terms.html into LANDING_ROOT at startup. The image
|
||||
# ships a baseline /srv/landing layout (assets, llms.txt) but
|
||||
# the HTML files are runtime-generated. Mount a writable
|
||||
# emptyDir over /srv/landing so the runtime can write, while
|
||||
# keeping the image's static layout via subPath copies — but
|
||||
# since the image already chowns /srv/landing to the landing
|
||||
# user, no overlay is needed for normal operation. The
|
||||
# readOnlyRootFilesystem flag is intentionally NOT set so
|
||||
# /srv/landing stays writable.
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
@@ -0,0 +1,79 @@
|
||||
# neuron-web-secrets — runtime secrets pulled from Vault by ESO.
|
||||
#
|
||||
# Phase 1 deployment of neuron-web on Legion. Only ANTHROPIC_API_KEY and
|
||||
# SUPABASE_ANON_KEY are critical for the page to render and the chat
|
||||
# widget to talk to Claude. Stripe + Resend + DocuSeal are pulled from
|
||||
# the existing marketing-test path so checkout flows can be exercised in
|
||||
# stage; webhook + DocuSeal tokens are present but the public webhook
|
||||
# routes still go to the Cloud Run prod URL until we cut over.
|
||||
#
|
||||
# Vault paths used:
|
||||
# secret/neuron-technologies/anthropic api_key
|
||||
# secret/neuron-technologies/supabase anon_key, service_role_key, project_url
|
||||
# secret/neuron-technologies/marketing-test stripe_*, etc.
|
||||
# secret/neuron-technologies/notifications resend_api_key
|
||||
#
|
||||
# To add a new secret without changing the ExternalSecret manifest:
|
||||
# vault kv patch secret/neuron-technologies/<path> key="value"
|
||||
# then add a new data: entry below.
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: neuron-web-secrets
|
||||
namespace: neuron-web
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: neuron-web-secrets
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
# Critical — chat widget + page render
|
||||
- secretKey: ANTHROPIC_API_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/anthropic
|
||||
property: api_key
|
||||
- secretKey: NEURON_LLM_0_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/anthropic
|
||||
property: api_key
|
||||
- secretKey: SUPABASE_ANON_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/supabase
|
||||
property: anon_key
|
||||
- secretKey: SUPABASE_SERVICE_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/supabase
|
||||
property: service_role_key
|
||||
- secretKey: SUPABASE_URL
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/supabase
|
||||
property: project_url
|
||||
# Stripe — test/stage keys
|
||||
- secretKey: STRIPE_PUBLISHABLE_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/marketing-test
|
||||
property: next_public_stripe_publishable_key
|
||||
- secretKey: STRIPE_SECRET_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/marketing-test
|
||||
property: stripe_secret_key
|
||||
- secretKey: STRIPE_WEBHOOK_SECRET
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/marketing-test
|
||||
property: stripe_webhook_secret
|
||||
- secretKey: STRIPE_PRICE_PROFESSIONAL
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/marketing-test
|
||||
property: stripe_price_professional
|
||||
- secretKey: STRIPE_PRICE_FOUNDING
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/marketing-test
|
||||
property: stripe_price_founding
|
||||
# Notifications
|
||||
- secretKey: RESEND_API_KEY
|
||||
remoteRef:
|
||||
key: secret/data/neuron-technologies/notifications
|
||||
property: resend_api_key
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: neuron-web
|
||||
namespace: neuron-web
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- web-stage.neuralplatform.ai
|
||||
secretName: neuron-web-tls
|
||||
rules:
|
||||
- host: web-stage.neuralplatform.ai
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: neuron-web
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- registry-pull-secret.yaml
|
||||
- externalsecret.yaml
|
||||
- service.yaml
|
||||
- deployment.yaml
|
||||
- ingress.yaml
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: neuron-web
|
||||
labels:
|
||||
managed-by: argo-cd
|
||||
tier: web
|
||||
@@ -0,0 +1,30 @@
|
||||
# registry-pull-secret — credentials for registry.neuralplatform.ai
|
||||
# pulled from Vault by ESO. Lets the deployment fetch
|
||||
# registry.neuralplatform.ai/neuron-web:* without baking creds into the manifest.
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: registry-pull-secret
|
||||
namespace: neuron-web
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: registry-pull-secret
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: |
|
||||
{"auths":{"registry.neuralplatform.ai":{"username":"{{ .registry_user }}","password":"{{ .registry_docker_token }}","auth":"{{ list .registry_user .registry_docker_token | join ":" | b64enc }}"}}}
|
||||
data:
|
||||
- secretKey: registry_user
|
||||
remoteRef:
|
||||
key: secret/data/mudcraft
|
||||
property: registry_user
|
||||
- secretKey: registry_docker_token
|
||||
remoteRef:
|
||||
key: secret/data/mudcraft
|
||||
property: registry_docker_token
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: neuron-web
|
||||
namespace: neuron-web
|
||||
labels:
|
||||
app: neuron-web
|
||||
spec:
|
||||
selector:
|
||||
app: neuron-web
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
||||
@@ -265,6 +265,17 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "legion" {
|
||||
service = "http://headscale.headscale.svc.cluster.local:8080"
|
||||
}
|
||||
|
||||
# web-stage.neuralplatform.ai — Phase 1 Legion deploy of neuron-web
|
||||
# (native El landing server). Routes via Traefik to the neuron-web
|
||||
# service in the neuron-web namespace.
|
||||
ingress_rule {
|
||||
hostname = "web-stage.neuralplatform.ai"
|
||||
service = "https://traefik.kube-system.svc:443"
|
||||
origin_request {
|
||||
no_tls_verify = true
|
||||
}
|
||||
}
|
||||
|
||||
# Catch-all — must be last
|
||||
ingress_rule {
|
||||
service = "http_status:404"
|
||||
|
||||
Reference in New Issue
Block a user