@premai/api-sdk
v1.0.65
Published
TypeScript client and local compatibility proxy for the [Prem Confidential API](https://docs.prem.io/).
Readme
@premai/api-sdk
TypeScript client and local compatibility proxy for the Prem Confidential API.
Documentation | Quickstart | API keys | npm
Choose an integration
| You want to | Use |
| --- | --- |
| Call the Confidential API from TypeScript | createRvencClient |
| Use a configurable OpenAI-compatible client | confidential-proxy --compat openai |
| Use a configurable Anthropic-compatible client | confidential-proxy --compat anthropic |
| Launch Claude Code through the local proxy | confidential-claude |
| Launch Qwen Code through the local proxy | confidential-qwen |
Install
npm install @premai/api-sdkThe SDK runs natively on Node.js, Bun, Deno, and Bare. It needs no native add-ons. The Bare runtime also powers the mobile client.
The package also publishes three command-line programs:
confidential-proxy: a local OpenAI and Anthropic-compatible HTTP server.confidential-claude: an interactive launcher for Claude Code.confidential-qwen: an interactive launcher for Qwen Code.
Prerequisites
- Create a Prem API key in the dashboard.
- Get the current Confidential API endpoints from
dashboard.prem.io/endpoints.json. - Generate a 32-byte Key Encryption Key (KEK), encoded as 64 hexadecimal characters:
export PREM_API_KEY="your-api-key"
export CLIENT_KEK="$(openssl rand -hex 32)"
export PROXY_URL="https://gateway.prem.io"
export ENCLAVE_URL="https://conf-engine.prem.io"Keep the KEK in a secret manager and reuse it. Do not commit the API key, the KEK, or the serialized DEK store to source control.
These variable names apply to the examples only:
PREM_API_KEY— the SDK does not read this variable. Pass its value asapiKey, or send it in the authentication header for the local proxy.CLIENT_KEK— an encryption key, not an API credential.PROXY_URL— the remote Prem gateway URL, not the localconfidential-proxylistener.
TypeScript quickstart
The TypeScript client performs encryption in the application process. You do not need to run confidential-proxy when you use this client directly.
import { createRvencClient } from "@premai/api-sdk";
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Set ${name}`);
return value;
}
async function main() {
const client = await createRvencClient({
apiKey: requireEnv("PREM_API_KEY"),
clientKEK: requireEnv("CLIENT_KEK"),
config: {
endpoints: {
proxy: requireEnv("PROXY_URL"),
enclave: requireEnv("ENCLAVE_URL"),
},
},
});
const models = await client.models.list({ type: "CHAT" });
const model = models.find((item) => item.enabled !== 0)?.model;
if (!model) {
throw new Error("No enabled chat model is available for this API key");
}
const response = await client.chat.completions.create({
model,
messages: [{ role: "user", content: "Reply with exactly OK." }],
max_completion_tokens: 256,
});
console.log(response.choices[0]?.message?.content);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});To get a list of available models, see List available models. For request and response details, see Chat completions.
Local compatibility proxy
Use the proxy when an application already supports an OpenAI or Anthropic base URL. The proxy listens on http://127.0.0.1:8787 by default.
npx -p @premai/api-sdk confidential-proxy \
--compat openai \
--kek "$CLIENT_KEK"The proxy reads PROXY_URL and ENCLAVE_URL from the environment. It does not read CLIENT_KEK automatically, so pass the KEK with --kek. It also does not read PREM_API_KEY. Each client must send its own API key with the request.
In another terminal, list the models available to the API key:
curl http://127.0.0.1:8787/v1/models \
-H "Authorization: Bearer $PREM_API_KEY"Then send a chat completion with an enabled chat model from that response:
export PREM_MODEL="your-enabled-chat-model"
curl http://127.0.0.1:8787/v1/chat/completions \
-H "Authorization: Bearer $PREM_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"$PREM_MODEL\",
\"messages\": [{\"role\": \"user\", \"content\": \"Reply with exactly OK.\"}],
\"max_completion_tokens\": 256
}"Compatibility modes
| Mode | Local routes |
| --- | --- |
| openai | /v1/* |
| anthropic | /v1/* |
| both | /openai/v1/* and /anthropic/v1/* |
Use --compat both only when one proxy process must expose both API formats.
Run the proxy as a daemon
The proxy runs in the foreground by default. Use these subcommands to run it in the background instead:
| Command | Purpose |
| --- | --- |
| confidential-proxy start | Starts the proxy as a background daemon |
| confidential-proxy status | Reports the process ID and whether the endpoint answers |
| confidential-proxy stop | Stops the daemon with a graceful shutdown |
start takes the same options as the foreground command. Optionally add --log-file to redirect the daemon's stdout and stderr to a file.
npx -p @premai/api-sdk confidential-proxy start \
--compat openai \
--kek "$CLIENT_KEK" \
--log-file ./proxy.log
npx -p @premai/api-sdk confidential-proxy status
npx -p @premai/api-sdk confidential-proxy stopstart returns only after the proxy answers on its port, so a script can start the daemon and then send requests. It waits up to 30 seconds. If the proxy does not answer, start prints the reason and exits non-zero. Check the --log-file output for details — the daemon reports startup errors there.
The proxy also writes <data-dir>/confidential-proxy.log in every mode. Use --log-level debug to add detail to that file.
| Option | Default | Commands |
| --- | --- | --- |
| --state-file <path> | <data-dir>/proxy.state.json | start, status, stop |
| --log-file <path> | None | start |
| --log-level <level> | info | start, foreground |
| --shutdown-timeout <ms> | 30000 | start, foreground |
<data-dir> is ~/Library/Application Support/confidential-proxy-nodejs on macOS and ~/.local/share/confidential-proxy-nodejs on Linux. Pass the same --state-file to every command. To run two daemons at the same time, give each one its own --port and --state-file.
--log-level accepts error, warn, info, http, verbose and debug. --shutdown-timeout sets how long the proxy waits for in-flight requests before it closes the listener.
The state file holds a shutdown token, do not move it to a shared location.
See Confidential Proxy for routes, TLS, CORS, Docker, and all CLI options.
Claude Code
confidential-claude starts or reuses the local proxy in Anthropic mode. It lists the enabled models, lets you select one, and launches the installed claude command against the local endpoint.
It needs Claude Code, an interactive terminal, and the same endpoints and keys as above. The launcher reads the Prem API key from API_KEY.
export API_KEY="$PREM_API_KEY"
npx -p @premai/api-sdk confidential-claudeThe launcher forwards extra arguments to Claude Code. To stop a proxy that the launcher started, run:
npx -p @premai/api-sdk confidential-proxy stopThe confidential boundary applies only to supported model traffic, after the local proxy encrypts it. Claude Code's repository access, shell commands, MCP servers, hooks, and tool results stay on the local machine or in their own external systems. See the Claude Code guide.
Qwen Code
confidential-qwen does the same for Qwen Code. It starts or reuses the local proxy in Anthropic mode, lists the enabled models, lets you select one, and launches the installed qwen command against the local endpoint.
It needs Qwen Code, an interactive terminal, and the same endpoints and keys as above. The launcher reads the Prem API key from API_KEY.
export API_KEY="$PREM_API_KEY"
npx -p @premai/api-sdk confidential-qwenThe launcher picks the model and the authentication type itself, so it drops --model, -m, and --auth-type from the arguments you pass. It forwards the rest to Qwen Code. Use confidential-proxy stop to stop a proxy that the launcher started.
The confidential boundary works the same way as for Claude Code. Qwen Code's repository access, shell commands, MCP servers, and tool results stay on the local machine or in their own external systems. See the Qwen Code guide.
API surface
The client exposes these primary namespaces:
client.chat.completions: encrypted chat completions, including streaming.client.models: models available to the API key.client.audio.transcriptions: encrypted audio transcription.client.audio.translations: encrypted audio translation. Confirm that an enabled model supports translation before you use it.client.files: encrypted file upload, listing, retrieval, indexing, and deletion.client.tools: encrypted tool requests supported by the account and runtime.
Use the public reference for request and response contracts:
Persist file keys
File and RAG operations update client.dekStore. Serialize the store after each write. Restore it when the application starts again:
import {
deserializeDEKStore,
serializeDEKStore,
} from "@premai/api-sdk";
const serialized = serializeDEKStore(client.dekStore);
// Store `serialized` in secure application storage.
const dekStore = deserializeDEKStore(serialized);
// Pass `dekStore` to createRvencClient on the next start.If you lose the KEK or the DEK store, you can no longer use the files that you uploaded before.
Security boundary
- The TypeScript process or the local proxy encrypts supported inference, audio, file, and tool payloads before they leave the machine. It does not encrypt model discovery or operational metadata.
- The Prem gateway receives ciphertext and operational metadata. It uses the metadata for authentication, billing, rate limits, routing, and session handling.
- Attestation is on by default. The client verifies hardware evidence and freshness for supported paths. The attestation reference lists the remaining measurement-policy limits. The SDK accepts
attest: falseand the proxy accepts--no-attest. Both flags turn off the default gate. - A client can still process plaintext before it calls the local proxy. Review the guide for that client before you treat the full workflow as confidential.
Read How it works, Security model, Encryption, and Attestation before you deploy the SDK in a sensitive workflow.
Mobile (React Native)
The SDK runs inside a react-native-bare-kit worklet. See the React Native guide for the worklet setup and the WASM asset path.
Configuration reference
TypeScript client
| Option | Required | Default | Purpose |
| --- | --- | --- | --- |
| apiKey | Yes | None | Prem API authentication |
| clientKEK | Yes unless CLIENT_KEK is set | CLIENT_KEK | Wraps file and RAG data-encryption keys; it is not an API credential |
| config.endpoints.proxy | Yes unless PROXY_URL is set | PROXY_URL | Prem gateway endpoint |
| config.endpoints.enclave | Yes unless ENCLAVE_URL is set | ENCLAVE_URL | Confidential runtime endpoint |
| attest | No | true | Runs the SDK's attestation gate |
| requestTimeoutMs | No | 600000 | Request timeout in milliseconds |
| maxBufferSize | No | 10485760 | Maximum streaming buffer size in bytes |
| dekStore | No | New store | Restores file and RAG key state |
Proxy defaults
| Setting | Default |
| --- | --- |
| Host | 127.0.0.1 |
| Port | 8787 |
| Compatibility mode | openai |
| JSON body limit | 32mb |
| Request shutdown timeout | 30000 ms |
Run confidential-proxy --help to see the options in the installed version.
