Skip to content

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":"superadmin@ask.mod.auh1.dev.dir",
        "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":"superadmin@ask.mod.auh1.dev.dir",
        "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