openova/platform/newapi/blueprint.yaml
e3mrah 20b3c5258a
feat(bp-newapi): chart maturation + first-otech deploy + Qwen vLLM channel (#799) (#812)
* 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>
2026-05-04 22:17:25 +04:00

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