Provider Key and Registry Maintenance
Operator procedure for provider key enablement and registry artifact import
Use this procedure for both the first provider enablement and every later provider-key or catalog update. It is an operator procedure, not a bootstrap script.
Two independent inputs are required before Nexus can route PTB traffic:
- A platform provider key lets Nexus authenticate to the upstream provider.
- A registry artifact supplies the provider’s models, capabilities, endpoints, and rates.
Loading a key does not make a model routable. Importing an artifact does not create a provider credential.
Execution context
Section titled “Execution context”Run the nexus commands below in a short-lived, operator-owned CLI Job in the target namespace.
The Job must have:
- the same
NEXUS_DATABASE_URL,NEXUS_TENANT_ID, andNEXUS_STATE_DIRas the deployed control plane; - the existing
nexus-statePVC mounted at/var/lib/nexus, so the CLI uses the deployed KMS wrapping key; - a read-only mount for the approved artifact and registry verification key; and
- a secret-manager projection for the provider key, available as a file or environment variable.
Do not run these commands in a long-lived application pod. Do not put provider secrets in Helm values, Git, shell arguments, or Job logs.
The examples assume:
export PROVIDER=openaiexport PROVIDER_KEY_FILE=/var/run/secrets/provider/openaiexport ARTIFACT=/work/artifacts/openai.jsonexport VERIFYING_KEY=/work/trust/registry-verifying-key.txtexport EXPECTED_KEY_ID=registry-productionCreate a one-shot CLI container
Section titled “Create a one-shot CLI container”Use a new Job name for each operation so its logs and audit record identify one change. The Kubernetes and OpenShift manifest is the same; OpenShift assigns the runtime UID through its restricted SCC.
For OpenShift, prefer the standalone runner in nexus-iac instead of authoring the Job manifest
each time. It creates a hardened one-shot Job, streams its logs, and deletes it after completion.
It accepts only references to pre-existing Secrets and ConfigMaps.
export NEXUS_NAMESPACE=nexus-platformexport NEXUS_TENANT_ID=tenant_...export NEXUS_CLI_IMAGE=registry.example/nexus-cli@sha256:<digest>
# Load a key from a Secret whose data contains OPENAI_PLATFORM_API_KEY.nexus-iac/scripts/openshift/nexus-cli.sh \ --provider-secret provider-openai -- \ credentials platform-keys set \ --provider openai \ --workspace ws_... \ --secret-env OPENAI_PLATFORM_API_KEY
# Plan an import from an immutable ConfigMap containing the artifact and trusted public key.nexus-iac/scripts/openshift/nexus-cli.sh \ --registry-inputs-configmap registry-inputs-<artifact-digest> -- \ registry import /work/registry/openai.json --dry-run \ --expected-key-id registry-production \ --verifying-key-path /work/registry/registry-verifying-key.txtUse --keep-job when retaining the completed Job is required for a change record. The invoking
OpenShift identity needs namespace-scoped permission to create, get, watch, read logs from, and
delete Jobs, and to read the referenced Secret, ConfigMap, and PVC metadata.
The example assumes the platform team has already delivered:
provider-openai, a namespace Secret containingOPENAI_PLATFORM_API_KEY;registry-inputs-<artifact-digest>, an immutable ConfigMap containingopenai.jsonandregistry-verifying-key.txt; and- a digest-pinned Nexus CLI image and the existing
nexus-statePVC.
Replace the image, tenant, workspace, secret names, and artifact ConfigMap name for the target:
apiVersion: batch/v1kind: Jobmetadata: name: nexus-provider-key-openai-20260802 namespace: nexus-platform labels: app.kubernetes.io/name: nexus-cli app.kubernetes.io/component: provider-maintenancespec: backoffLimit: 0 ttlSecondsAfterFinished: 3600 template: metadata: labels: app.kubernetes.io/name: nexus-cli app.kubernetes.io/component: provider-maintenance spec: restartPolicy: Never automountServiceAccountToken: false securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: cli image: registry.example/nexus-cli@sha256:<digest> command: ["/usr/local/bin/nexus"] args: - credentials - platform-keys - set - --provider - openai - --workspace - ws_... - --label - secret-version=<approved-version> - --secret-env - OPENAI_PLATFORM_API_KEY envFrom: - secretRef: name: provider-openai env: - name: POSTGRES_USER valueFrom: secretKeyRef: name: platform-postgres-credentials key: username - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: platform-postgres-credentials key: password - name: NEXUS_DATABASE_URL value: postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@platform-postgres:5432/nexus_control - name: AUTH_DATABASE_URL value: postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@platform-postgres:5432/nexus_auth - name: NEXUS_TENANT_ID value: tenant_... - name: NEXUS_STATE_DIR value: /var/lib/nexus volumeMounts: - name: state mountPath: /var/lib/nexus - name: registry-inputs mountPath: /work/registry readOnly: true resources: requests: cpu: 10m memory: 32Mi limits: cpu: 500m memory: 256Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsNonRoot: true volumes: - name: state persistentVolumeClaim: claimName: nexus-state - name: registry-inputs configMap: name: registry-inputs-<artifact-digest>Create and inspect the key-operation Job:
kubectl apply -f provider-key-job.yamlkubectl wait --for=condition=complete job/nexus-provider-key-openai-20260802 \ --namespace nexus-platform --timeout=5mkubectl logs job/nexus-provider-key-openai-20260802 --namespace nexus-platform
# OpenShift uses the same manifest and commands:oc apply -f provider-key-job.yamloc wait --for=condition=complete job/nexus-provider-key-openai-20260802 \ --namespace nexus-platform --timeout=5moc logs job/nexus-provider-key-openai-20260802 --namespace nexus-platformFor a registry import, copy the manifest, use a new Job name, remove envFrom, and replace args
with the approved import command:
args: - registry - import - /work/registry/openai.json - --yes - --expected-key-id - registry-production - --verifying-key-path - /work/registry/registry-verifying-key.txtUse the same Job shape for platform-keys rotate, platform-keys validate, registry import --dry-run, and registry models list; change only metadata.name and args.
For Docker Compose, the CLI profile starts the equivalent one-shot container on the Compose network:
# From nexus/. The artifact path is relative to the repository mounted at /workspace.docker compose --env-file .env \ -f deploy/compose/docker-compose.yml -f deploy/compose/docker-compose.dev.yml \ --profile cli run --rm -i \ -e OPENAI_PLATFORM_API_KEY \ cli cargo run -p nexus-cli -- \ credentials platform-keys set \ --provider openai --workspace ws_... \ --secret-env OPENAI_PLATFORM_API_KEY1. Load or update the platform key
Section titled “1. Load or update the platform key”For the first load, set the intended scope:
nexus credentials platform-keys set \ --provider "$PROVIDER" \ --workspace ws_... \ --label "secret-version=<approved-version>" \ --secret-file "$PROVIDER_KEY_FILE"For an ongoing secret rotation, update the existing key ID:
nexus credentials platform-keys rotate ppk_... \ --secret-file "$PROVIDER_KEY_FILE"Verify the stored credential and inspect the active key:
nexus credentials platform-keys validate --provider "$PROVIDER"nexus credentials platform-keys validate --id ppk_... --livenexus credentials platform-keys list --provider "$PROVIDER" --workspace ws_...--live probes OpenAI or Anthropic; other providers are decrypt-only. set revokes an active key
at the same provider/scope before replacing it, while rotate keeps the same key ID and replaces its
secret. The current CLI has no parallel active keys at one scope, so schedule the provider-side
rotation window accordingly.
Use rm only to revoke a key after traffic is intentionally disabled or moved:
nexus credentials platform-keys rm ppk_...These are PTB credentials. Tenant BYOK credentials use nexus credentials provider-keys.
2. Generate a candidate artifact
Section titled “2. Generate a candidate artifact”Generate the artifact outside the cluster in a controlled build environment, not in the Nexus runtime. Keep the artifact, provenance, digest, and signature in the approved artifact store.
just registry-generate \ --generated-at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --artifact-id "provider-catalog-$(date -u +%Y%m%d)" \ --signing-key /secure/path/registry-signing-key.txt \ --key-id "$EXPECTED_KEY_ID" \ --output dist/catalogs
just registry-import "dist/catalogs/$PROVIDER.json" --check \ --expected-key-id "$EXPECTED_KEY_ID" \ --verifying-key-path /secure/path/registry-verifying-key.txtFor a custom or isolated source, build the same artifact from reviewed CSV inputs:
just registry-from-csv "$PROVIDER" catalog.csv rates.csv "dist/catalogs/$PROVIDER.json"An artifact can give an existing adapter backend a distinct provider name. The name is the registry, credential, policy, billing, and telemetry identity; the typed provider remains the adapter backend. For example, an operator-authored xAI catalog can reuse the OpenAI-compatible adapter without adding a provider implementation:
nexus-registry-generator from-csv \ --provider openai \ --name xai \ --catalog xai-catalog.csv \ --rates xai-rates.csv \ --output dist/catalogs/xai.json
nexus registry import dist/catalogs/xai.json --yesnexus credentials platform-keys set \ --provider xai \ --secret-env XAI_API_KEY \ --base-url https://api.x.ai/v1After the import and the gateway’s next registry refresh, clients select the lane with a model string such as xai/grok-4.5.
Custom names are one model-route segment: they cannot be empty, contain whitespace, /, or ,, use the reserved nexus name, or reuse a built-in provider name for a different backend.
All models and rate cards under one custom name share the artifact’s backend.
Artifacts are complete provider snapshots. Any non-pinned endpoint absent from the next artifact is retired.
3. Plan the import in the target cluster
Section titled “3. Plan the import in the target cluster”Copy or mount the exact approved artifact and verifying key into the CLI Job, then validate it again in the target context and produce the durable import plan:
nexus registry import "$ARTIFACT" --check \ --expected-key-id "$EXPECTED_KEY_ID" \ --verifying-key-path "$VERIFYING_KEY"
nexus registry import "$ARTIFACT" --dry-run --format json \ --expected-key-id "$EXPECTED_KEY_ID" \ --verifying-key-path "$VERIFYING_KEY"Review the plan before applying it. Record the artifact SHA-256, signature key ID, provider,
generation timestamp, and every retire or pricing/capability change. A hard-reject finding cannot
be approved; correct the artifact and repeat the procedure.
4. Apply the reviewed update
Section titled “4. Apply the reviewed update”Apply the same bytes that were planned:
nexus registry import "$ARTIFACT" \ --expected-key-id "$EXPECTED_KEY_ID" \ --verifying-key-path "$VERIFYING_KEY"The command asks for confirmation. For a non-interactive operator Job, use --yes only after the
review record identifies this exact artifact digest and plan:
nexus registry import "$ARTIFACT" --yes \ --expected-key-id "$EXPECTED_KEY_ID" \ --verifying-key-path "$VERIFYING_KEY"Import is transactional and fails closed on altered artifacts, stale baselines, expired approvals, signature failures, or invalid pricing. Correct an applied registry update by importing a new compensating artifact; do not edit historical rate-card rows.
5. Verify and close the change
Section titled “5. Verify and close the change”nexus registry models listnexus credentials platform-keys list --provider "$PROVIDER"Confirm the expected endpoint is eligible and that Gateway routing/telemetry shows successful provider traffic. Gateways poll committed registry state; no Gateway restart is required after an artifact import.
How a request picks among providers
Section titled “How a request picks among providers”One canonical model may be served by several providers at once, each its own registry row with its own price. Two rules decide which one a request reaches:
- A provider-pinned model string —
openai/gpt-5.6-terra,bedrock-gov/gpt-5.1— resolves that provider’s endpoint or fails with an unknown-provider error. No other provider is tried. - An unpinned model string —
gpt-5.6-terra— fans out across every provider serving that model and orders the attempts cheapest first, using each endpoint’s own prompt plus completion rate.
Because ordering is by price alone, a row seeded with a placeholder or mistyped rate becomes the
first attempt for every request that names its model. Check the seeded prices before enabling a
provider, and read them back with nexus registry models list.
To keep an organization’s unpinned traffic off a provider entirely — reserving government capacity is the usual reason — add that provider to the organization’s provider disallow list. Disallowed providers are dropped from the candidate set before any credential is fetched, for pinned and unpinned requests alike.
Local development
Section titled “Local development”The manual commands are identical in local Docker Compose; run them through the Compose CLI:
# From nexus/just load-provider-keysjust cli registry import dist/catalogs/openai.json --dry-runjust cli registry import dist/catalogs/openai.jsonThe .env-based Docker Desktop and CRC helpers are for local testing only, not shared-cluster
maintenance.
