Use Celestialization in OpenCode
The Celestialization API speaks the OpenAI chat completions format, so OpenCode can use it as a normal provider. Point OpenCode at the endpoint, give it an API key, and pick a model.
| Item | Value |
|---|---|
| Base URL | https://api.celestialization.com/v1 |
| Chat endpoint | POST https://api.celestialization.com/v1/chat/completions |
| Model list | GET https://api.celestialization.com/v1/models (public, no key needed) |
| Authentication | Authorization: Bearer <API_KEY> |
| API key | Contact the developer for the API key. Keys are issued per person or per application. |
| Request / response | JSON in, JSON out. stream: true returns Server-Sent Events. |
Models
| Model ID (send this) | Upstream model | Notes |
|---|---|---|
celestial-gemini |
Gemini 3.5 Flash Lite | Default. Fast, strong tool calling. |
celestial-groq |
Llama 3.3 70B on Groq | Fallback when Gemini is rate limited. Short prompts only. |
celestial-deepseek |
DeepSeek Flash | Long context, lower cost. |
celestial-deepseek-pro |
DeepSeek V4 Pro | Long context, strongest DeepSeek option. |
The proxy falls back to the next provider automatically when one is unavailable, so you normally only choose the model once.
OpenCode reads opencode.json or opencode.jsonc. The global file applies to
every project:
| Platform | Path |
|---|---|
| Windows | %USERPROFILE%\.config\opencode\opencode.json |
| macOS / Linux | ~/.config/opencode/opencode.json |
| Per project | opencode.json or .opencode/opencode.json in the project folder |
Add this provider and keep whatever else is already in the file:
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"celestial": {
"name": "Celestialization",
"package": "@opencode/ai/providers/openai-compatible",
"settings": {
"baseURL": "https://api.celestialization.com/v1",
"apiKey": "<YOUR_API_KEY>"
},
"models": {
"celestial-gemini": {
"name": "Celestial Gemini",
"modelID": "celestial-gemini",
"limit": { "context": 256000, "output": 32000 }
},
"celestial-groq": {
"name": "Celestial Groq",
"modelID": "celestial-groq",
"limit": { "context": 128000, "output": 8192 }
},
"celestial-deepseek": {
"name": "Celestial DeepSeek",
"modelID": "celestial-deepseek",
"limit": { "context": 1048576, "output": 32768 }
},
"celestial-deepseek-pro": {
"name": "Celestial DeepSeek Pro",
"modelID": "celestial-deepseek-pro",
"limit": { "context": 1048576, "output": 32768 }
}
}
}
}
}
Keep secrets out of version control. Instead of pasting the key, you can point at an environment
variable: "apiKey": "{env:CELESTIAL_API_KEY}", or at a file:
"apiKey": "{file:~/.secrets/celestial-key}".
Either set a default in the same file:
{
"model": "celestial/celestial-gemini"
}
…or run /models inside OpenCode and choose a Celestialization model from
the list. Config changes are picked up on save; use /reload if you want to force it.
Run this before blaming the editor:
curl https://api.celestialization.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CELESTIAL_API_KEY" \
-d '{
"model": "celestial-gemini",
"messages": [{ "role": "user", "content": "Say OK" }],
"max_tokens": 16
}'
A working key returns choices[0].message.content. A wrong or missing key returns HTTP
401 with "code": "invalid_api_key".
You do not have to edit JSON by hand. OpenCode can add the provider, validate the file, and test the
endpoint for you. Start a session, paste the prompt below, and replace
<YOUR_API_KEY> with the key the developer gave you.
Configure OpenCode to use the Celestialization API as a provider, then verify it.
Endpoint
- Provider id: celestial
- Display name: Celestialization
- Base URL: https://api.celestialization.com/v1
- Chat endpoint: POST https://api.celestialization.com/v1/chat/completions
- Models: GET https://api.celestialization.com/v1/models
- Package: @opencode/ai/providers/openai-compatible
- Auth header: Authorization: Bearer <YOUR_API_KEY>
- If <YOUR_API_KEY> is still a placeholder, ask me for the key first. Never print the key back or commit it.
Models (OpenCode id -> id sent upstream)
- celestial-gemini -> celestial-gemini (256000 context, 32000 output)
- celestial-groq -> celestial-groq (128000 context, 8192 output)
- celestial-deepseek -> celestial-deepseek (1048576 context, 32768 output)
- celestial-deepseek-pro-> celestial-deepseek-pro (1048576 context, 32768 output)
Do this:
1. Open my global OpenCode config: ~/.config/opencode/opencode.json
(on Windows: %USERPROFILE%\.config\opencode\opencode.json). Create it if missing and keep the $schema line.
2. Keep every existing provider, model, agent, command, plugin, MCP server and permission exactly as it is.
Only add or update providers.celestial.
3. Add the provider block:
"celestial": {
"name": "Celestialization",
"package": "@opencode/ai/providers/openai-compatible",
"settings": {
"baseURL": "https://api.celestialization.com/v1",
"apiKey": "<YOUR_API_KEY>"
},
"models": {
"celestial-gemini": { "name": "Celestial Gemini", "modelID": "celestial-gemini", "limit": { "context": 256000, "output": 32000 } },
"celestial-groq": { "name": "Celestial Groq", "modelID": "celestial-groq", "limit": { "context": 128000, "output": 8192 } },
"celestial-deepseek": { "name": "Celestial DeepSeek", "modelID": "celestial-deepseek", "limit": { "context": 1048576, "output": 32768 } },
"celestial-deepseek-pro": { "name": "Celestial DeepSeek Pro", "modelID": "celestial-deepseek-pro", "limit": { "context": 1048576, "output": 32768 } }
}
}
4. Validate the file is still valid JSON/JSONC and that the schema URL is intact.
5. Check for a stale saved credential for provider "celestial" and remove it, because a saved
credential overrides settings.apiKey and can make requests go out with an empty key:
- Run: opencode auth list
- If a Celestialization / celestial entry appears, run:
opencode auth logout celestial "Celestialization"
(if it asks for a credential ID or label, pass the label exactly as listed)
- Then run: opencode auth list and confirm no celestial entry remains.
6. Do not change my default model unless I ask. If I say "make it the default", set "model": "celestial/celestial-gemini".
7. Verify the endpoint without changing any other setting:
curl https://api.celestialization.com/v1/models
curl https://api.celestialization.com/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer <YOUR_API_KEY>" -d '{"model":"celestial-gemini","messages":[{"role":"user","content":"Say OK"}],"max_tokens":16}'
opencode run --model celestial/celestial-gemini "Reply with exactly: OK"
Report the HTTP status of both calls and the assistant text from the second and third ones.
8. Tell me what you changed, and remind me that config reloads on save (use /reload to force it).
The prompt is deliberately explicit: it names the provider id, the exact base URL, the package, and
every model id, so OpenCode does not have to guess an endpoint or invent a model name. It also tells
OpenCode to preserve your existing providers and to verify with plain curl before
reporting success.
- Streaming:
stream: truereturns a normal SSE stream, so OpenCode shows tokens as they arrive. - Tool calling: OpenAI-style
tools/tool_callswork, including multi-turn tool loops, so file edits and shell commands behave normally. - Fallback: Gemini → Groq → DeepSeek. If one provider is rate limited or unavailable the proxy retries the next one; the response reports which provider answered in
provider. - Long prompts: Groq is skipped automatically for prompts that are too large instead of failing the request.
- Models: unknown model ids are answered with the default Gemini model, so a typo quietly changes the model — check the
modelfield in the response if answers look different.
Troubleshooting
| Symptom | Fix |
|---|---|
HTTP 401, invalid_api_key |
The key is missing or wrong. Send Authorization: Bearer <key>. Contact the developer if you do not have one. |
| OpenCode says “Model unavailable: celestial/…” | The model id must match a key in providers.celestial.models. Remove any custom modelID you added by hand. |
| “Cannot initialize provider … baseURL” | The base URL must end at /v1, without /chat/completions. |
| Timeouts on long answers | Raise the provider timeouts: "settings": { "headerTimeout": 600000, "chunkTimeout": 60000 }. |
| Works in curl, not in OpenCode | OpenCode reads config on save. Run /reload, then pick the model again from /models. |
| Still asked for a key, or the server reports “Missing API key” | A saved credential is overriding settings.apiKey (often an empty one). Run opencode auth list, then opencode auth logout <provider-id> "<label>". See below. |
If OpenCode keeps asking for the key
OpenCode resolves a key in this order: saved credential → provider settings.apiKey
→ environment variable. A saved credential always wins, so an old or empty one can quietly
override the key you put in the config file. The request then reaches the API with
Authorization: Bearer and no value, which the server correctly rejects with 401.
# 1. See what is stored (one line per credential)
opencode auth list
# 2. Remove the stale entry for this provider.
# With no terminal prompt available, pass the label too.
opencode auth logout celestial "Celestialization"
# 3. Confirm the config file is the only key source
opencode auth list # celestial should no longer appear
opencode models | grep celestial
# 4. Test against the live endpoint
opencode run --model celestial/celestial-gemini "Reply with exactly: OK"
If you instead prefer storing the key in OpenCode's credential store, run /connect (or
opencode auth login) and paste the key — but keep only one source of truth. Having both a
saved credential and a config key is what causes this confusion. Escape hatch: a provider-level
"headers": { "Authorization": "Bearer <key>" } is also honoured, but only use it
after removing the saved credential, otherwise the header is sent twice.
Getting a key
Access is issued manually so usage can be tracked per client. Contact the developer for the API key and mention whether you need the Gemini, Groq or DeepSeek models. Do not share a key between projects, and rotate it if it is ever exposed in a repository, screenshot or chat log.