seal

How it works

Markdown

Using a blob

Your app opens a session on a blob and gets a local URL and a stand-in key. The enclave adds the real key on the way out.

What your app needs#

The blob
Where it comes fromWherever you stored it, like OPENAI_API_BLOB.
SEAL_ENDPOINT
Where it comes fromYour replicas, comma-separated. Shown on the console's Connect page.
SEAL_TENANT
Where it comes fromYour tenant id. acme is enough for sessions.
An identity
Where it comes fromYour cloud's identity, or SEAL_APP_KEY. See Apps and identities.

The SDK reads these itself. You only pass it the blob.

HTTP APIs#

The session gives you a local URL and a stand-in key. Use them as your client's base URL and API key.

import os, seal
from openai import OpenAI

with seal.session(os.environ["OPENAI_API_BLOB"], kind="http") as s:
    client = OpenAI(base_url=s.url, api_key=s.key)
    client.responses.create(model="gpt-5", input="Hello")

The URL already includes the sealed path, like /v1. kind is optional and catches a database blob passed by mistake. await using needs Node 24 or TypeScript 5.2; otherwise call await s.close() in a finally.

Databases#

For Postgres and Redis, the session gives you a connection string for your driver.

import os, seal, psycopg

with seal.session(os.environ["DATABASE_BLOB"], kind="database") as db:
    conn = psycopg.connect(db.dsn)

The string points at 127.0.0.1 with sslmode=disable, because that hop never leaves your machine. The hop to the enclave is TLS. The enclave logs in as the user and database in the blob.

What happens on each request#

Your server

AppYour code
SDKTunnel
TEEEnclave
Upstream OpenAI

POST http://127.0.0.1:40213/responses

authorization: Bearer sl_9f…stand-in

FigureYour code makes a call. The OpenAI client sends a normal request to the session's local URL, with the stand-in key where the real one used to go.

On open, the SDK checks the enclave's attestation and sends the blob with a proof of identity. The enclave checks the identity's grant, network rules, the blob's expiry and key version, suspension and the artifact check.

The enclave sends the secret only to the blob's destination. Other paths, denied paths and disallowed methods get a 403, and the upstream never sees them. WebSockets, compressed responses and range requests don't pass through. See APIs and databases.

Keeping a session#

  • Open one per blob at startup and keep it. Opening one per request is slow.
  • It renews itself every five minutes with the same checks. Open connections carry on.
  • Settings changes apply live. Remove a grant and its sessions stop within about 30 seconds, open connections included.
  • Closing ends it on every replica. Leave the with block, await using scope or defer, or call close(). If close() fails, call it again.
  • Failover is built in. With several replicas in SEAL_ENDPOINT, the SDK moves on when one stops answering. After two minutes with none, it raises Unavailable.
  • The first call after 15 idle minutes takes about a minute while Seal's hosted replica starts. Your own replicas never stop on their own.
  • Forked workers like gunicorn or Celery must open sessions after the fork. In gunicorn, use post_fork or the worker's first request.
  • New blobs reach a running process through reload_with and on_reseal in Python, Node and Rust. See Rotating and revoking.

Errors#

The SDK raises typed errors, each with a retryable flag. Errors lists them all and what to do.