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
|
proxied = true
|
||||||
ttl = 1
|
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"
|
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
|
# Catch-all — must be last
|
||||||
ingress_rule {
|
ingress_rule {
|
||||||
service = "http_status:404"
|
service = "http_status:404"
|
||||||
|
|||||||
Reference in New Issue
Block a user