Set Up CDN Static Assets Bucket (Model Logos)¶
Needed by: Frontend (browser renders logo_path URLs from the model catalog).
Generated by: core-service — every model in GET /api/v1/models/ returns a logo_path like
https://cdn.ask.mod.auh1.dev.dir/static/logos/llm_models/<name>.svg.
Blocks: Model icons appear broken in the UI until this bucket exists.
Why this exists¶
core-service builds logo URLs using cdn_base_url from its TOML config:
logo_path = cdn_base_url + "/static/logos/llm_models/" + logo_file_name
= "https://cdn.ask.mod.auh1.dev.dir" + "/static/logos/llm_models/openai.svg"
The browser fetches these at page load. If the host doesn't exist or the file is missing, model icons show as broken images (functionality unaffected; purely cosmetic until the UI ships).
Logos required (extracted 2026-06-23 from live catalog)¶
9 unique SVG files — verified via GET /api/v1/models/ across all 40 catalog models:
| File | Used by |
|---|---|
claude.svg |
anthropic/claude-* models |
falcon.svg |
tiiuae/falcon3-, ai71-agrillm/falcon3- |
google.svg |
google-gemini-2-5-flash |
llama.svg |
meta/llama3-3-70b, ai71-agrillm/llama3* |
minimax.svg |
minimax-m2-* |
openai.svg |
gpt-4o, gpt-5*, gpt-oss-120b |
qwen.svg |
qwen2.5-, qwen3-, ai71-agrillm/qwen* |
xai.svg |
grok-4* |
zai.svg |
glm-4-7, glm-5 |
Re-extract if the catalog grows:
kubectl exec -i -n ask deploy/assistant-service-intelligence -- env PW="$SUPERPW" python3 - <<'EOF'
import os, httpx
CS="http://core-service.ask.svc.cluster.local"
with httpx.Client(base_url=CS, timeout=20) as c:
r=c.post("/api/v1/auth/login", json={
"org_id":"378447542100166005",
"email":"[email protected]",
"password":os.environ["PW"]})
tok=r.json().get("sessionToken")
h={"Authorization":"Bearer "+tok}
models=c.get("/api/v1/models/", headers=h).json()
logos=sorted({mm.get("logo_file_name") for t in ["inference","embedding"]
for mm in models.get(t,[]) if mm.get("logo_file_name")})
print(f"{len(logos)} logos:", logos)
EOF
Step 1 — Collect the SVG files (Mac)¶
5 available locally in the read-only source checkout:
ls /Users/tarun.mittal/Documents/096_RKE/modgpt/ask-core-service/assets/logos/
# claude.svg falcon.svg llama.svg openai.svg qwen.svg
4 missing — extract from the running container (core-service has them baked in):
# Find the logos path inside the running pod
kubectl exec -n ask deploy/core-service -- find /app -name "openai.svg" 2>/dev/null
# Once path is confirmed (e.g. /app/src/core_service/static/logos/llm_models/), copy all 4
for f in google minimax xai zai; do
kubectl cp ask/$(kubectl get pod -n ask -l app.kubernetes.io/name=core-service \
-o jsonpath='{.items[0].metadata.name}'):/app/src/core_service/static/logos/llm_models/${f}.svg \
/tmp/logos/${f}.svg
done
ls /tmp/logos/
Collect all 9 into one directory on the Mac:
mkdir -p /tmp/ask-logos
cp /Users/tarun.mittal/Documents/096_RKE/modgpt/ask-core-service/assets/logos/*.svg /tmp/ask-logos/
# copy the 4 extracted above
cp /tmp/logos/*.svg /tmp/ask-logos/
ls /tmp/ask-logos/ # should show all 9
Step 2 — Create the Ceph S3 bucket (bastion)¶
The cluster already has a Ceph RGW S3 endpoint at https://s3.cl1.sq4.aegis.internal.
Create a dedicated public-read bucket for CDN assets:
# Get Ceph RGW admin credentials (from Vault or the rook-ceph dashboard secret)
export AWS_ACCESS_KEY_ID=$(vault kv get -field=access_key secret/ceph/rgw-admin)
export AWS_SECRET_ACCESS_KEY=$(vault kv get -field=secret_key secret/ceph/rgw-admin)
export S3_ENDPOINT="https://s3.cl1.sq4.aegis.internal"
# Create bucket
aws s3 mb s3://ask-cdn-static \
--endpoint-url $S3_ENDPOINT \
--no-verify-ssl
# Set public-read policy (allows browser to fetch logos without auth)
cat > /tmp/bucket-policy.json <<'POLICY'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::ask-cdn-static/*"
}]
}
POLICY
aws s3api put-bucket-policy \
--bucket ask-cdn-static \
--policy file:///tmp/bucket-policy.json \
--endpoint-url $S3_ENDPOINT \
--no-verify-ssl
Step 3 — Upload logos (Mac → bastion → bucket)¶
Copy SVGs to bastion then upload:
# From Mac — scp to bastion
scp /tmp/ask-logos/*.svg cloud-user@bastion1:/tmp/ask-logos/
# On bastion — upload under the correct S3 prefix
aws s3 cp /tmp/ask-logos/ \
s3://ask-cdn-static/static/logos/llm_models/ \
--recursive \
--content-type "image/svg+xml" \
--endpoint-url $S3_ENDPOINT \
--no-verify-ssl
# Verify
aws s3 ls s3://ask-cdn-static/static/logos/llm_models/ \
--endpoint-url $S3_ENDPOINT \
--no-verify-ssl
Expected output — 9 .svg files listed.
Step 4 — DNS entry for cdn.ask.mod.auh1.dev.dir¶
Add to the cluster DNS zone (same HAProxy VIP as the other *.ask.mod.auh1.dev.dir hosts):
| Host | Points to |
|---|---|
cdn.ask.mod.auh1.dev.dir |
HAProxy VIP 100.115.2.210 |
Also add to /etc/hosts on the Mac if testing logo URLs directly from the browser:
# Already present if you followed 41-frontend-deploy.md Phase 2.5 — check:
grep cdn /etc/hosts
# If missing:
sudo sh -c 'echo "100.115.2.210 cdn.ask.mod.auh1.dev.dir" >> /etc/hosts'
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder 2>/dev/null
Step 5 — Nginx Ingress for the CDN host¶
Create an Ingress that routes cdn.ask.mod.auh1.dev.dir to the Ceph RGW S3 endpoint.
The simplest approach is an nginx proxy_pass to the RGW bucket URL:
# cdn-ingress.yaml — apply in the ask namespace (or a dedicated cdn namespace)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cdn-static
namespace: ask
annotations:
cert-manager.io/cluster-issuer: internal-ca-issuer
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /ask-cdn-static/$request_uri
nginx.ingress.kubernetes.io/proxy-pass-headers: "Content-Type"
nginx.ingress.kubernetes.io/upstream-vhost: "s3.cl1.sq4.aegis.internal"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-ssl-verify: "off"
spec:
ingressClassName: nginx
tls:
- secretName: cdn-tls
hosts: [cdn.ask.mod.auh1.dev.dir]
rules:
- host: cdn.ask.mod.auh1.dev.dir
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cdn-s3-proxy # ExternalName service pointing to RGW
port:
number: 443
Alternative: direct RGW virtual-host style
If Ceph RGW supports virtual-hosted bucket URLs, the browser can hit
https://ask-cdn-static.s3.cl1.sq4.aegis.internal/static/logos/llm_models/openai.svg directly.
Set cdn_base_url = "https://ask-cdn-static.s3.cl1.sq4.aegis.internal" in the TOML and skip the Ingress.
Requires the wildcard cert to cover *.s3.cl1.sq4.aegis.internal or TLS bypass at the browser.
Step 6 — Smoke test¶
Once the ingress is up:
# From bastion (in-cluster)
curl -sk https://cdn.ask.mod.auh1.dev.dir/static/logos/llm_models/openai.svg | head -c 100
# From Mac (checks DNS + TLS path)
curl -sk --cacert ~/internal-ca.crt \
https://cdn.ask.mod.auh1.dev.dir/static/logos/llm_models/openai.svg | head -c 100
# Should start with: <svg ...
Also confirm via the model catalog:
kubectl exec -i -n ask deploy/assistant-service-intelligence -- env PW="$SUPERPW" python3 - <<'EOF'
import os, httpx
CS="http://core-service.ask.svc.cluster.local"
with httpx.Client(base_url=CS, timeout=20) as c:
r=c.post("/api/v1/auth/login", json={
"org_id":"378447542100166005",
"email":"[email protected]",
"password":os.environ["PW"]})
tok=r.json().get("sessionToken")
h={"Authorization":"Bearer "+tok}
models=c.get("/api/v1/models/enabled", headers=h).json()
for m in models.get("inference",[]):
url=m.get("logo_path","")
r2=httpx.get(url, verify=False, timeout=5)
print(m["id"], "→", r2.status_code, url.split("/")[-1])
EOF
All entries should return 200.
Known deviations¶
| Deviation | Production-correct | Air-gap workaround |
|---|---|---|
| Public-read bucket policy | Signed CDN URLs (CloudFront/CloudFlare) | Public-read on Ceph RGW bucket (logos are non-sensitive) |
| TLS for RGW | Valid public cert | Internal CA + proxy-ssl-verify: off in Ingress |