# 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.

Source: https://seal.zone/docs/sessions

## What your app needs

| What | Where it comes from |
|---|---|
| The blob | Wherever you stored it, like `OPENAI_API_BLOB`. |
| `SEAL_ENDPOINT` | Your replicas, comma-separated. Shown on the console's **Connect** page. |
| `SEAL_TENANT` | Your tenant id. `acme` is enough for sessions. |
| An identity | Your cloud's identity, or `SEAL_APP_KEY`. See [Apps and identities](https://seal.zone/docs/apps). |

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.
```python
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")
```

```typescript
import { Client } from "@sealzone/sdk";
import OpenAI from "openai";

const seal = await Client.fromEnv();
await using s = await seal.session(process.env.OPENAI_API_BLOB!, { kind: "http" });
const openai = new OpenAI({ baseURL: s.url, apiKey: s.key });
await openai.responses.create({ model: "gpt-5", input: "Hello" });
```

```go
client, err := seal.FromEnv(ctx)
if err != nil {
	return err
}
s, err := client.Session(ctx, os.Getenv("OPENAI_API_BLOB"), seal.WithKind(seal.KindHTTP))
if err != nil {
	return err
}
defer s.Close(ctx)
// Send requests to s.URL() with "Authorization: Bearer " + s.Key()
```

```rust
let seal = seal::Client::from_env().await?;
let blob = std::env::var("OPENAI_API_BLOB")?;
let s = seal.session(&blob).http().await?;
// Send requests to s.url() with s.key() as the bearer token
s.close().await?;
```
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.
```python
import os, seal, psycopg

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

```typescript
import { Client } from "@sealzone/sdk";
import pg from "pg";

const seal = await Client.fromEnv();
await using db = await seal.session(process.env.DATABASE_BLOB!, { kind: "database" });
const conn = new pg.Client({ connectionString: db.dsn });
await conn.connect();
```

```go
// import _ "github.com/jackc/pgx/v5/stdlib"
s, err := client.Session(ctx, os.Getenv("DATABASE_BLOB"), seal.WithKind(seal.KindDatabase))
if err != nil {
	return err
}
defer s.Close(ctx)
conn, err := sql.Open("pgx", s.DSN())
```

```rust
let blob = std::env::var("DATABASE_BLOB")?;
let db = seal.session(&blob).database().await?;
let (conn, task) = tokio_postgres::connect(db.dsn(), tokio_postgres::NoTls).await?;
tokio::spawn(task);
```
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

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](https://seal.zone/docs/apps#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](https://seal.zone/docs/upstreams).

## 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](https://seal.zone/docs/revoking).

## Errors

The SDK raises typed errors, each with a `retryable` flag. [Errors](https://seal.zone/docs/errors) lists them all and what to do.
