Skip to content

Commit e272a9d

Browse files
committed
feat(providers): add Oracle Cloud Infrastructure Generative AI provider profile
Add an example oci-genai inference profile for OCI Generative AI through its OpenAI-compatible endpoint. The profile injects a compartment-scoped Generative AI API key as a bearer token only at the regional OCI inference hosts and only under /openai/v1, so the sandbox never holds the key and the key cannot reach any other OCI surface. Register the profile id in the telemetry provider bucket and the provider profile listing test, add a row to the inference provider table, and add a docs page covering the OCI authentication transports and how each maps to OpenShell today, the policy-before-key IAM ordering, least-privilege policy statements, key rotation, regions and realms, and troubleshooting. Follows the deepinfra precedent: example YAML, telemetry bucket, overview row, docs page. No new mechanism. Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
1 parent cf1bbb9 commit e272a9d

6 files changed

Lines changed: 266 additions & 0 deletions

File tree

‎crates/openshell-core/src/telemetry.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ pub enum ProviderProfile {
203203
Github,
204204
Gitlab,
205205
Nvidia,
206+
OciGenai,
206207
Openai,
207208
Opencode,
208209
Outlook,
@@ -221,6 +222,7 @@ impl ProviderProfile {
221222
Self::Github => "github",
222223
Self::Gitlab => "gitlab",
223224
Self::Nvidia => "nvidia",
225+
Self::OciGenai => "oci-genai",
224226
Self::Openai => "openai",
225227
Self::Opencode => "opencode",
226228
Self::Outlook => "outlook",
@@ -239,6 +241,7 @@ impl ProviderProfile {
239241
"github" | "gh" => Self::Github,
240242
"gitlab" | "glab" => Self::Gitlab,
241243
"nvidia" => Self::Nvidia,
244+
"oci-genai" | "oci" => Self::OciGenai,
242245
"openai" => Self::Openai,
243246
"opencode" => Self::Opencode,
244247
"outlook" => Self::Outlook,

‎crates/openshell-server/src/grpc/provider.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5122,6 +5122,7 @@ fn telemetry_provider_profile(provider_type: &str) -> TelemetryProviderProfile {
51225122
Some("deepinfra") => TelemetryProviderProfile::Deepinfra,
51235123
Some("github") => TelemetryProviderProfile::Github,
51245124
Some("nvidia") => TelemetryProviderProfile::Nvidia,
5125+
Some("oci-genai") => TelemetryProviderProfile::OciGenai,
51255126
Some("openai") => TelemetryProviderProfile::Openai,
51265127
_ => TelemetryProviderProfile::Custom,
51275128
}
@@ -6301,6 +6302,7 @@ mod tests {
63016302
"google-cloud",
63026303
"google-vertex-ai",
63036304
"nvidia",
6305+
"oci-genai",
63046306
"openai",
63056307
"openrouter",
63066308
"pypi"
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
title: "Oracle"
5+
sidebar-title: "Oracle"
6+
description: "Reach OCI Generative AI from OpenShell sandboxes with a compartment-scoped Generative AI API key that the proxy injects only at the OCI inference endpoint."
7+
keywords: "Generative AI, Oracle Cloud, OCI, OCI Generative AI, API Key, OpenAI-compatible, Credentials, Sandbox"
8+
---
9+
10+
The `oci-genai` provider gives sandboxes governed access to
11+
[OCI Generative AI](https://docs.oracle.com/en-us/iaas/Content/generative-ai/home.htm)
12+
through its OpenAI-compatible endpoint. The sandbox receives a placeholder in
13+
`OCI_GENAI_API_KEY`; the sandbox proxy swaps in the real Generative AI API key
14+
only on requests to the regional OCI inference host, and only under
15+
`/openai/v1`. The agent never holds the key, and the key is useless anywhere
16+
else.
17+
18+
## OCI Authentication Transports
19+
20+
OCI clients authenticate in several ways. This table shows how each maps to
21+
OpenShell today.
22+
23+
| OCI transport | Typical client | OpenShell support |
24+
|---|---|---|
25+
| Generative AI API key (`sk-...`, bearer token on `/openai/v1`) | OpenAI SDKs, curl, any OpenAI-compatible agent | Supported by the `oci-genai` profile on this page. |
26+
| API key request signing (user principal, RSA HTTP `Signature` header) | OCI CLI, OCI SDKs, `langchain-oci` `auth_type=API_KEY` | Not yet. Needs proxy-side OCI request signing, similar to [AWS SigV4](/how-it-works/providers/aws). |
27+
| Security token (session from `oci session authenticate`) | OCI CLI, `langchain-oci` `auth_type=SECURITY_TOKEN` | Not yet. Same signing gap, plus an `external` refresh of the token file. |
28+
| Instance principal, resource principal, OKE workload identity | Compute, Functions, OKE workloads | Not yet. Same signing gap, plus a gateway refresh strategy that federates the principal. |
29+
30+
Use the Generative AI API key transport for inference. For the native
31+
`/20231130` Generative AI API, Object Storage, or any other OCI service, wait
32+
for proxy-side request signing or front the service with a bridge that signs in
33+
its own workload and exposes a bearer-authenticated endpoint to the sandbox.
34+
35+
## Prerequisites
36+
37+
- An OCI tenancy with Generative AI available in your region. Regional
38+
availability and the model list served through the OpenAI-compatible
39+
endpoint are in the
40+
[OCI Generative AI documentation](https://docs.oracle.com/en-us/iaas/Content/generative-ai/home.htm).
41+
- The [OCI CLI](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm)
42+
configured with an identity that can manage policies and Generative AI API
43+
keys in the target compartment.
44+
- A running OpenShell gateway (see [Quick Start](/about/quickstart)).
45+
46+
## Create the IAM Policy First
47+
48+
A Generative AI API key is an IAM principal of type `generativeaiapikey`. It
49+
only works after a policy admits that principal type. Create the policy before
50+
the key: a key created before its policy can stay unauthorized well after the
51+
policy lands, and OCI returns the same `401` for an unknown key and an
52+
unauthorized one, so the response will not tell you which it is.
53+
54+
Scope the policy to one compartment and the one principal type. Do not grant
55+
`manage` when `use` is enough:
56+
57+
```shell
58+
COMPARTMENT_ID=ocid1.compartment.oc1..example
59+
60+
oci iam policy create \
61+
--compartment-id "$COMPARTMENT_ID" \
62+
--name openshell-genai-api-keys \
63+
--description "Generative AI API keys in this compartment may call Generative AI" \
64+
--statements "[\"allow any-user to use generative-ai-family in compartment id $COMPARTMENT_ID where ALL {request.principal.type='generativeaiapikey'}\"]"
65+
```
66+
67+
To pin the policy to a single key after it exists, add
68+
`request.principal.id='<api-key-ocid>'` to the `where` clause.
69+
70+
## Create the Generative AI API Key
71+
72+
Create the key in the same compartment the policy covers, in the region you
73+
will call. Set an expiry; the secret is returned once, in the create response,
74+
and cannot be retrieved later:
75+
76+
```shell
77+
REGION=us-chicago-1
78+
79+
oci generative-ai api-key create \
80+
--compartment-id "$COMPARTMENT_ID" \
81+
--region "$REGION" \
82+
--display-name openshell-agents \
83+
--key-details '[{"keyName":"primary","timeExpiry":"2027-01-01T00:00:00Z"}]'
84+
```
85+
86+
Copy the `sk-...` secret from the response into a password manager or a
87+
gateway credential store. Rotate it with
88+
`oci generative-ai api-key renew` and disable it with
89+
`oci generative-ai api-key set-api-key-state`; both take effect without
90+
touching sandboxes because the gateway, not the sandbox, holds the value.
91+
92+
## Import the Profile and Create a Provider
93+
94+
Import the `oci-genai` profile; a gateway serves only the profiles you imported:
95+
96+
```shell
97+
openshell provider profile import --url https://raw.githubusercontent.com/NVIDIA/OpenShell/main/providers/oci-genai.yaml --global
98+
```
99+
100+
Create a provider from the key. Read the secret from your password manager or
101+
an environment variable rather than typing it into the shell history:
102+
103+
```shell
104+
openshell provider create \
105+
--name my-oci-genai \
106+
--type oci-genai \
107+
--credential OCI_GENAI_API_KEY="$OCI_GENAI_API_KEY"
108+
```
109+
110+
The key is compartment-scoped, so no `opc-compartment-id` header or
111+
compartment configuration is needed.
112+
113+
## Use from a Sandbox
114+
115+
Create a sandbox with the provider attached and call the endpoint. The proxy
116+
resolves the `OCI_GENAI_API_KEY` placeholder only for the OCI host declared by
117+
the profile:
118+
119+
```shell
120+
openshell sandbox create --name oci-demo --provider my-oci-genai
121+
122+
openshell sandbox exec --name oci-demo -- sh -c '
123+
curl -sS --compressed \
124+
https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1/chat/completions \
125+
-H "Authorization: Bearer $OCI_GENAI_API_KEY" \
126+
-H "Content-Type: application/json" \
127+
-d "{\"model\":\"meta.llama-3.3-70b-instruct\",\"messages\":[{\"role\":\"user\",\"content\":\"Reply with OK\"}]}"
128+
'
129+
```
130+
131+
Any OpenAI SDK works unchanged. Point it at the regional base URL and pass the
132+
placeholder as the API key:
133+
134+
```shell
135+
export OPENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
136+
export OPENAI_API_KEY="$OCI_GENAI_API_KEY"
137+
```
138+
139+
Model identifiers use the OCI form, such as `meta.llama-3.3-70b-instruct`,
140+
`openai.gpt-oss-120b`, or `xai.grok-4`. The Chat Completions and Responses
141+
APIs are served under `/openai/v1`. Not every model behind the native API is
142+
reachable through the OpenAI-compatible endpoint with an API key; check the
143+
OCI documentation for the current list.
144+
145+
The profile ships `curl` as its only client binary. Add your interpreter or
146+
agent CLI to `binaries` in a copy of the profile before importing it, or the
147+
credential is never injected for that process.
148+
149+
## Regions and Realms
150+
151+
The profile's host pattern `inference.generativeai.*.oci.oraclecloud.com`
152+
matches every region in the commercial realm; `*` matches exactly one DNS
153+
label, which is the region identifier. Tenancies in other realms, such as
154+
government realms, use a different domain. Copy the profile and add an endpoint
155+
entry with that realm's host before importing.
156+
157+
Dedicated AI cluster endpoints use the same regional host, so the profile
158+
covers them as well.
159+
160+
## Security Notes
161+
162+
- **Least privilege on both sides.** The IAM policy admits only
163+
`generativeaiapikey` principals in one compartment, and the profile grants
164+
only `/openai/v1` on the regional inference host. A leaked placeholder cannot
165+
be used from outside the sandbox, and the real key cannot reach any other
166+
OCI service.
167+
- **Expiry and rotation.** Set `timeExpiry` on every key. Rotate with
168+
`oci generative-ai api-key renew` and update the provider with
169+
`openshell provider update`; running sandboxes pick up the new value through
170+
the normal placeholder path.
171+
- **Auditing.** Calls made with the key are attributed to the key's OCID in
172+
OCI Audit. Use one key per agent fleet or per workspace so audit entries map
173+
to an OpenShell provider.
174+
- **No compartment header.** Because the key is compartment-scoped, sandboxes
175+
never learn a compartment OCID. Keep it that way; do not add
176+
`opc-compartment-id` to the endpoint rules.
177+
178+
## Troubleshooting
179+
180+
- **`401` on every request.** Either the key is unknown or it is not yet
181+
authorized. Confirm the policy exists in the key's compartment and was
182+
created before the key. If the key was created first, mint a new key rather
183+
than waiting.
184+
- **Binary garbage instead of JSON.** The endpoint compresses responses. Pass
185+
`--compressed` to curl, or let your SDK negotiate encoding.
186+
- **`GET /openai/v1/models` returns `404`.** Model listing is not served on
187+
this endpoint. Do not use it as a health check; send a small chat request
188+
instead.
189+
- **Request denied by the sandbox proxy.** Check that the calling binary is in
190+
the profile's `binaries` list and that the request path starts with
191+
`/openai/v1`. Use `openshell sandbox logs` to see the denial.
192+
- **Native API calls fail.** `/20231130` endpoints require OCI request
193+
signing, which this profile does not provide. See the transport table above.

‎docs/how-it-works/providers/overview.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ selects the model and request timeout.
470470
| Anthropic | `anthropic` | `https://api.anthropic.com` | `ANTHROPIC_API_KEY` |
471471
| NVIDIA API Catalog | `nvidia` | `https://integrate.api.nvidia.com/v1` | `NVIDIA_API_KEY` |
472472
| DeepInfra | `deepinfra` | `https://api.deepinfra.com/v1/openai` | `DEEPINFRA_API_KEY` |
473+
| Oracle Cloud Infrastructure Generative AI | `oci-genai` | `https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1` | `OCI_GENAI_API_KEY` |
473474
| Google Vertex AI | `google-vertex-ai` | Region and model dependent | `GOOGLE_VERTEX_AI_TOKEN` or `GOOGLE_VERTEX_AI_SERVICE_ACCOUNT_TOKEN` |
474475

475476
An OpenAI-compatible protocol does not make the `openai` profile safe for an

‎docs/index.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ navigation:
5555
path: how-it-works/providers/aws.mdx
5656
- page: "Google"
5757
path: how-it-works/providers/google.mdx
58+
- page: "Oracle"
59+
path: how-it-works/providers/oracle.mdx
5860
- section: "Policies"
5961
slug: policies
6062
contents:

‎providers/oci-genai.yaml‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Example provider profile. OpenShell does not load it; import it explicitly:
5+
# openshell provider profile lint -f providers/oci-genai.yaml
6+
# openshell provider profile import -f providers/oci-genai.yaml --global
7+
#
8+
# Copy and edit this file rather than importing it unchanged. `binaries` is the
9+
# least-privilege control that decides which processes may reach the endpoints
10+
# below, so it has to name the paths in *your* image.
11+
#
12+
# Client binaries: curl. Add the interpreter or agent CLI that calls the
13+
# OpenAI-compatible endpoint (python, node, ...) before
14+
# relying on binary-scoped attribution for this profile.
15+
# Reference layout: any image with curl or an OpenAI SDK. Point the SDK at
16+
# https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1
17+
# and pass OCI_GENAI_API_KEY as the API key; the sandbox
18+
# proxy resolves the placeholder only at the hosts below.
19+
# Credential scope: one OCI Generative AI API key (sk-...), created with
20+
# `oci generative-ai api-key create`. The key is scoped to
21+
# the compartment it was created in, so requests do not
22+
# need an opc-compartment-id header. Create the IAM policy
23+
# that admits `request.principal.type='generativeaiapikey'`
24+
# BEFORE creating the key; a key minted before its policy
25+
# can stay unauthorized for a long time.
26+
# Endpoint access: the regional OCI Generative AI inference hosts in the
27+
# commercial realm (oraclecloud.com), TLS terminated, L7
28+
# enforced, limited to the OpenAI-compatible /openai/v1
29+
# surface. Generative AI API keys are only valid there;
30+
# the native /20231130 API requires OCI request signing,
31+
# which this profile does not grant. Other realms use their
32+
# realm domain: add a matching endpoint entry.
33+
# Smoke test: openshell sandbox create --provider <name> -- \
34+
# curl -sS --compressed \
35+
# https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1/chat/completions \
36+
# -H "Authorization: Bearer $OCI_GENAI_API_KEY" \
37+
# -H 'Content-Type: application/json' \
38+
# -d '{"model":"meta.llama-3.3-70b-instruct","messages":[{"role":"user","content":"Reply with OK"}]}'
39+
40+
id: oci-genai
41+
display_name: Oracle Cloud Infrastructure Generative AI
42+
description: OCI Generative AI inference through the OpenAI-compatible endpoint with a compartment-scoped Generative AI API key
43+
category: inference
44+
inference_capable: true
45+
credentials:
46+
- name: api_key
47+
description: OCI Generative AI API key (compartment-scoped, sent as a bearer token)
48+
env_vars: [OCI_GENAI_API_KEY]
49+
required: true
50+
auth_style: bearer
51+
header_name: authorization
52+
discovery:
53+
credentials: [api_key]
54+
endpoints:
55+
# `*` matches exactly one DNS label, which is the region identifier, for
56+
# example us-chicago-1, eu-frankfurt-1, or ap-osaka-1.
57+
- host: "inference.generativeai.*.oci.oraclecloud.com"
58+
port: 443
59+
protocol: rest
60+
enforcement: enforce
61+
rules:
62+
- allow: { method: POST, path: "/openai/v1/**" }
63+
- allow: { method: GET, path: "/openai/v1/**" }
64+
- allow: { method: DELETE, path: "/openai/v1/**" }
65+
binaries: [/usr/bin/curl, /usr/local/bin/curl]

0 commit comments

Comments
 (0)