* feat(bp-newapi): chart maturation — ExternalSecret + first-otech vLLM channel + skip-render gates (#799) Maturation work for the SME-3 turnkey-experience epic (#795). Aligns the bp-newapi scratch chart with ADR-0003 (RBAC ↔ NewAPI user-create hook contract) and gets it past the blueprint-release CI smoke render that has blocked publication since PR #396 (run 25213444992 failed at default-values render of v1.0.0). Changes ------- - templates/external-secret.yaml (NEW). Renders the `catalyst-newapi-admin-token` ExternalSecret consumed by unified-rbac (ADR-0003 §3.2 + §6) for issuing per-user keys against `http://newapi.newapi.svc/api/v1/admin/users`. Sourced from OpenBao via the `vault-region1` ClusterSecretStore (canonical default shipped by bp-external-secrets-stores). Capabilities-gated on `external-secrets.io/v1beta1` so cold installs without ESO don't fail-render. Operator supplies the per-Sovereign OpenBao path via `catalystIntegration.externalSecret.remoteRef.key`; canonical convention is `sovereign/<sovereign-fqdn>/newapi/admin-token` with property `ADMIN_API_TOKEN`. Per Inviolable Principle #4 every knob is operator-overridable in the cluster overlay. - values.yaml. Adds `catalystIntegration.externalSecret.{enabled, refreshInterval, secretStoreRef.{kind,name}, remoteRef.{key,property}}` block (default enabled=true, key="" so a misconfigured overlay fails loudly at render rather than silently skipping). Adds `defaultChannels.vllm` block — first-otech shorthand that composes a vLLM-typed channel into the rendered channels list when enabled. Default endpoint is empty per Inviolable Principle #4; the `clusters/<sovereign>/bootstrap-kit/80-newapi.yaml` overlay supplies the per-Sovereign URL (canonical first-otech reference = `https://llm-api.omtd.bankdhofar.com` model `qwen3-coder`, the same upstream Axon uses on the OpenOva marketing deployment). - templates/_helpers.tpl. New `bp-newapi.effectiveChannels` helper composes `.Values.channels` with `defaultChannels.vllm` (when enabled). The `assertChannelAttestation` helper now operates on the effective list so attestation gates apply to defaultChannels composition too. `defaultChannels.vllm.enabled=true` with empty endpoint fails-fast at render with a guided error message. - templates/configmap.yaml. Channels rendering switches to the effectiveChannels helper. OIDC block now skip-renders gracefully when `auth.adminUI.keycloak.issuer` is unset (smoke-render path) instead of `required`-failing; the per-Sovereign overlay sets the issuer. - templates/deployment.yaml. Skip-render gate on Deployment when `database.existingSecret`, `credentials.existingSecret`, or (when Keycloak mode is selected) the OIDC client secret is missing. Removes the four `required` calls that were failing CI smoke render. Service, ServiceAccount, ConfigMap, NetworkPolicy still render so the smoke test gets a non-empty output proving structural soundness; the actual Deployment defers until the per-Sovereign overlay wires the secrets. - templates/ingress.yaml. Same skip-render pattern: when either `ingress.host` or `ingress.adminHost` is empty, the entire ingress block is silently skipped. Matches the bp-keycloak / bp-openbao / bp-external-dns HTTPRoute templates. - Chart.yaml. version 1.0.0 → 1.1.0 (minor bump — additive features; no breaking changes to existing operator overrides). Verification ------------ `helm template` smoke render on default values now succeeds with 4 resources (NetworkPolicy / ServiceAccount / ConfigMap / Service); 168 lines, well above the CI 5-line minimum. With a full per-Sovereign overlay (hosts + secrets + Keycloak issuer + ESO Capabilities + Traefik Capabilities + defaultChannels.vllm.endpoint), 8 resources render including Deployment, both Ingresses, the Traefik allowlist Middleware, and the ExternalSecret. The composed qwen channel writes through to `channels.yaml` with the expected endpoint + models + attestation. Refs ---- ADR-0003 §3.2 + §6 — admin-token contract Issue #795 (epic) — locked decisions Issue #796 — hook contract spec (sequential blocker, merged) Inviolable Principles #1, #3, #4 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(bootstrap-kit): slot 80 — bp-newapi default install (#799) Adds the canonical install slot for bp-newapi to every fresh Sovereign's bootstrap-kit. Sequenced after the W2.K1 dependency wave so NewAPI's ExternalSecret + Postgres DSN dependencies resolve on first reconcile. The HelmRelease declares `dependsOn: [bp-openbao, bp-keycloak, bp-cnpg]`: - bp-openbao(08): admin-token ExternalSecret backend - bp-keycloak(09): OIDC issuer for ops-staff admin UI at admin.<fqdn> - bp-cnpg(16): Postgres backing for users/credits/channels/audit Per-Sovereign overlays inherit the slot's defaults and override: - ingress.host api.${SOVEREIGN_FQDN} - ingress.adminHost admin.${SOVEREIGN_FQDN} - auth.adminUI.keycloak.issuer - database.existingSecret (Crossplane-claimed) - credentials.existingSecret - catalystIntegration.externalSecret.remoteRef.key sovereign/${FQDN}/newapi/admin-token - defaultChannels.vllm.enabled true (first-otech) - defaultChannels.vllm.endpoint (operator-supplied) The `_template/` slot keeps `defaultChannels.vllm.enabled: false` so a fresh Sovereign does not silently wire customers to a third-party endpoint; the canonical first-otech reference (Qwen3 Coder via `https://llm-api.omtd.bankdhofar.com`, same relay Axon uses on the OpenOva marketing deployment) is documented in-line for operators adopting the same upstream. Refs: #795 (epic), ADR-0003 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(bootstrap-deps): register bp-newapi slot 80 in expected DAG (#799) Fixes the dependency-graph-audit drift detection caught at PR #812 CI: the audit script enumerates HelmReleases in clusters/_template/bootstrap-kit/ and compares to scripts/expected-bootstrap-deps.yaml; an HR present on disk but absent from the expected DAG is treated as drift. Adds the canonical entry for bp-newapi at slot 80 with the same depends_on set declared on the HelmRelease itself ([bp-openbao, bp-keycloak, bp-cnpg]). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(bp-newapi): align blueprint.yaml spec.version with Chart.yaml (#799) The TestBootstrapKit_BlueprintCardsHaveRequiredFields static-validation gate asserts Chart.yaml version == blueprint.yaml spec.version. The chart was bumped to 1.1.0 in c63ecd8c; bumping the blueprint metadata to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Hatice Yildiz <hatice.yildiz@openova.io> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
259 lines
9.9 KiB
YAML
259 lines
9.9 KiB
YAML
apiVersion: catalyst.openova.io/v1alpha1
|
|
kind: Blueprint
|
|
metadata:
|
|
name: bp-newapi
|
|
labels:
|
|
catalyst.openova.io/category: ai-runtime
|
|
catalyst.openova.io/section: pts-4-6-llm-serving
|
|
spec:
|
|
version: 1.1.0
|
|
card:
|
|
title: NewAPI
|
|
summary: |
|
|
Multi-tenant LLM marketplace gateway. Wraps the upstream NewAPI
|
|
(github.com/Calcium-Ion/new-api, MIT, fork of OneAPI) with credits,
|
|
per-key budgets, channel routing, and BYOK as first-class primitives.
|
|
Deployed in **backend-only** mode: the OpenAI-compatible API at
|
|
`api.<host>/v1/*` is customer-facing; the upstream's portal UI is
|
|
disabled at ingress; Catalyst replaces it as the customer surface;
|
|
NewAPI's admin UI at `admin.<host>` is exposed only to ops staff
|
|
(IdP-gated). Compliance posture (channel attestation, geographic
|
|
AUP, BYOK isolation, reseller disclosure) enforced at the
|
|
blueprint layer. Use `bp-newapi` when the Sovereign operator's
|
|
business model is reselling LLM access to its own customers; use
|
|
`bp-llm-gateway` for self-consumption.
|
|
icon: newapi.svg
|
|
category: ai-runtime
|
|
tags: [llm, gateway, marketplace, multi-tenant, credits, byok, openai-compatible, reseller]
|
|
documentation: https://github.com/Calcium-Ion/new-api
|
|
license: MIT
|
|
visibility: listed
|
|
owner:
|
|
team: ai-platform
|
|
contact: ai-platform@openova.io
|
|
configSchema:
|
|
type: object
|
|
properties:
|
|
replicas:
|
|
type: integer
|
|
default: 1
|
|
minimum: 1
|
|
maximum: 16
|
|
ingress:
|
|
type: object
|
|
required: [host, adminHost]
|
|
properties:
|
|
host:
|
|
type: string
|
|
description: Customer-facing API hostname (operator-supplied, e.g. api.<sovereign-fqdn>).
|
|
adminHost:
|
|
type: string
|
|
description: Ops-staff admin UI hostname (operator-supplied, e.g. admin.<sovereign-fqdn>). IdP-gated.
|
|
tlsIssuer:
|
|
type: string
|
|
default: letsencrypt-prod
|
|
description: cert-manager ClusterIssuer name.
|
|
database:
|
|
type: object
|
|
required: [existingSecret]
|
|
properties:
|
|
existingSecret:
|
|
type: string
|
|
description: ExternalSecret name holding the Postgres DSN (key SQL_DSN). Provisioned via a Crossplane PostgresqlInstance claim against bp-cnpg.
|
|
valkey:
|
|
type: object
|
|
properties:
|
|
url:
|
|
type: string
|
|
default: redis://valkey.valkey.svc.cluster.local:6379
|
|
description: Valkey URL for session and rate-limit cache.
|
|
auth:
|
|
type: object
|
|
properties:
|
|
adminUI:
|
|
type: object
|
|
properties:
|
|
mode:
|
|
type: string
|
|
enum: [keycloak, masterKey]
|
|
default: keycloak
|
|
description: |
|
|
`keycloak` = OIDC-only via bp-keycloak realm. `masterKey` = static master
|
|
key (DEV/BOOTSTRAP ONLY — operators MUST migrate to keycloak before exposing
|
|
admin.<host> on a public ingress).
|
|
keycloak:
|
|
type: object
|
|
properties:
|
|
issuer:
|
|
type: string
|
|
description: Keycloak realm issuer URL (e.g. https://keycloak.<sovereign>/realms/<ops-realm>).
|
|
clientId:
|
|
type: string
|
|
default: newapi-admin
|
|
customerAPI:
|
|
type: object
|
|
properties:
|
|
keyIssuer:
|
|
type: string
|
|
enum: [catalyst, self-serve]
|
|
default: catalyst
|
|
description: |
|
|
`catalyst` (DEFAULT) = customer keys are issued by Catalyst's signup hook
|
|
via NewAPI's admin API; the upstream's customer portal UI is disabled.
|
|
`self-serve` = the upstream's customer portal UI is enabled (NOT
|
|
recommended in Catalyst Sovereigns — Catalyst is the customer surface).
|
|
channels:
|
|
type: array
|
|
description: |
|
|
LLM provider channels exposed by this NewAPI instance. Every channel MUST carry
|
|
a verifiable `attestation` block; the blueprint refuses to render a Deployment
|
|
if any enabled channel lacks attestation.
|
|
items:
|
|
type: object
|
|
required: [name, type, attestation]
|
|
properties:
|
|
name:
|
|
type: string
|
|
description: Channel name (customer-visible model prefix, e.g. "qwen", "claude", "gpt").
|
|
type:
|
|
type: string
|
|
enum: [vllm, openai-compatible, anthropic, byok-passthrough]
|
|
description: |
|
|
`vllm` = in-cluster bp-vllm (cheap tier).
|
|
`openai-compatible` = commercial OpenAI/Together/Fireworks/etc.
|
|
`anthropic` = direct Anthropic API contract (operator's commercial account).
|
|
`byok-passthrough` = customer-supplied key, request-scoped, never aggregated.
|
|
endpoint:
|
|
type: string
|
|
description: Upstream endpoint URL (omit for byok-passthrough — taken from request).
|
|
existingSecret:
|
|
type: string
|
|
description: ExternalSecret name holding the upstream API key (omit for vllm and byok-passthrough).
|
|
models:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Model identifiers exposed via this channel.
|
|
attestation:
|
|
type: object
|
|
required: [kind]
|
|
properties:
|
|
kind:
|
|
type: string
|
|
enum: [in-cluster, commercial-contract, byok]
|
|
description: |
|
|
`in-cluster` = operator owns the model weights and inference hardware.
|
|
`commercial-contract` = operator holds a paid account at the upstream
|
|
provider with reseller terms reviewed by counsel.
|
|
`byok` = key is supplied per-request by the end customer.
|
|
accountId:
|
|
type: string
|
|
description: Provider-side account identifier (required for commercial-contract).
|
|
contractRef:
|
|
type: string
|
|
description: Internal document reference to the signed reseller terms (required for commercial-contract).
|
|
geo:
|
|
type: object
|
|
properties:
|
|
sanctionedRegions:
|
|
type: object
|
|
properties:
|
|
block:
|
|
type: array
|
|
items:
|
|
type: string
|
|
default: ["IR", "KP", "SY", "CU", "RU-occupied-UA"]
|
|
description: ISO-3166-1 alpha-2 region codes blocked on commercial-provider channels (US/EU export-control baseline).
|
|
allowRelaxation:
|
|
type: boolean
|
|
default: false
|
|
description: Set true ONLY with a recorded justification reviewed by counsel.
|
|
aup:
|
|
type: object
|
|
properties:
|
|
enforcement:
|
|
type: string
|
|
enum: [provider-strictest, per-channel, off]
|
|
default: provider-strictest
|
|
description: |
|
|
`provider-strictest` = downstream requests must satisfy the strictest AUP
|
|
across all enabled commercial channels. `per-channel` = each channel's
|
|
requests must satisfy that channel's upstream AUP. `off` = no enforcement
|
|
(open-weight in-cluster channels only — never set in a Sovereign with
|
|
commercial channels enabled).
|
|
audit:
|
|
type: object
|
|
properties:
|
|
enabled:
|
|
type: boolean
|
|
default: true
|
|
retentionDays:
|
|
type: integer
|
|
default: 730
|
|
description: Audit log retention on the bp-cnpg Postgres (days).
|
|
logPromptContent:
|
|
type: boolean
|
|
default: false
|
|
description: |
|
|
Default false (metadata-only). Operator may opt in per their privacy posture
|
|
and customer ToS — DO NOT enable without explicit customer disclosure.
|
|
byok:
|
|
type: object
|
|
properties:
|
|
scope:
|
|
type: string
|
|
enum: [request-scoped, never]
|
|
default: request-scoped
|
|
description: |
|
|
`request-scoped` (DEFAULT) = BYOK keys are passed through per-request and
|
|
never persisted, cached, or shared. `never` = BYOK is disabled entirely.
|
|
Cross-customer BYOK aggregation is not a supported value.
|
|
reseller:
|
|
type: object
|
|
properties:
|
|
disclosureRequired:
|
|
type: boolean
|
|
default: true
|
|
description: |
|
|
The operator must publish a `/legal/llm-providers` page listing the
|
|
upstream providers they resell. The blueprint's compliance smoke test
|
|
asserts the page is reachable at install time.
|
|
catalystIntegration:
|
|
type: object
|
|
properties:
|
|
enabled:
|
|
type: boolean
|
|
default: true
|
|
description: |
|
|
When true, the blueprint provisions a Kubernetes secret
|
|
`catalyst-newapi-admin-token` consumed by Catalyst's signup hook to issue
|
|
per-user API keys against NewAPI. Disable only in Sovereigns where Catalyst
|
|
is not the customer surface (rare).
|
|
placementSchema:
|
|
modes: [single-region, active-active]
|
|
default: single-region
|
|
manifests:
|
|
chart: ./chart
|
|
depends:
|
|
- blueprint: bp-cnpg
|
|
version: ^1.0
|
|
alias: db
|
|
- blueprint: bp-keycloak
|
|
version: ^1.0
|
|
alias: idp
|
|
- blueprint: bp-external-secrets
|
|
version: ^1.0
|
|
alias: eso
|
|
- blueprint: bp-valkey
|
|
version: ^1.0
|
|
alias: cache
|
|
- blueprint: bp-vllm
|
|
version: ^1.0
|
|
alias: vllm
|
|
optional: true
|
|
upgrades:
|
|
from: ["0.x"]
|
|
observability:
|
|
metrics: prometheus
|
|
logs: stdout
|