# APIs and databases

What Seal can sit in front of today: HTTPS APIs, Postgres and Redis, and what each one allows.

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

The upstream is the service your key unlocks. You name it when you seal, and the enclave uses the key there and nowhere else. Every upstream must speak TLS.

| Upstream | Seal it with `--to` | Your app gets |
| --- | --- | --- |
| HTTPS API, key in a header | `https://api.example.com/v1` | a local `url` and a stand-in `key` |
| Postgres | `postgres://app@db.example.com:5432/prod` | a local `dsn` |
| Redis | `rediss://cache.example.com:6380/0` | a local `dsn` |

Anyone can read a blob's destination without opening it.

## HTTP APIs

By default the enclave sends the key as `Authorization: Bearer <key>`, which OpenAI and Stripe expect. Your app sends the stand-in key, and the enclave swaps in the real one.

- `--header` picks another header. A few, such as `Host` and `Cookie`, aren't allowed.
- `--format` sets the value, with `{key}` exactly once. The default is `Bearer {key}` for `Authorization` and `{key}` for anything else.

```shell
seal secret --to https://api.openai.com/v1 --app checkout < openai-key.txt > openai.blob
seal secret --to https://api.anthropic.com --app checkout --header x-api-key < anthropic-key.txt > anthropic.blob
```

Pass the session's `url` to your client as its base URL. It keeps the sealed path, so seal Anthropic's bare host, since its libraries add `/v1`. The SDKs take the same options:
```python
blob = seal.seal(key, to="https://api.anthropic.com", apps=["checkout"], header="x-api-key")
```

```typescript
const blob = await seal.seal(key, { to: "https://api.anthropic.com", apps: ["checkout"], header: "x-api-key" });
```

```go
blob, err := client.Seal(ctx, []byte(key), "https://api.anthropic.com", []string{"checkout"}, seal.WithHeader("x-api-key"))
```

```rust
let blob = seal.seal(key, "https://api.anthropic.com", ["checkout"]).header("x-api-key").await?;
```
### Narrowing what a key can reach

A session can make any request the key allows. Narrow the key at the provider, then the blob:

| Option | Effect |
| --- | --- |
| A path in `--to`, like `/v1` | Only `/v1` and below, by whole segment: `/v1/chat`, not `/v10`. Anything else gets a 403 |
| `--methods GET,POST` | Only these methods |
| `--deny /v1/organization` | Refuses that prefix, ignoring case. Use it for key-management endpoints |

Paths with percent-encoding, `..` or `//` are always refused. With `--methods`, so are `multipart/form-data` bodies.

### Response masking

The enclave masks any copy of the key in a response with `*`. It finds plain text, one layer of JSON or URL escaping, and base64, including inside `Basic` auth and JWTs.

It misses hashes, hex, partial keys and double escaping. It's a backstop. It's also why Seal refuses secrets under 16 bytes: a short key could be guessed by watching which echo gets masked.

## What the enclave changes about requests

| Your app sends | The enclave |
| --- | --- |
| Any `Host` header | Sets it to the destination's host and port |
| A request that gets a redirect | Returns the 3xx. It never follows redirects |
| A WebSocket or other `Upgrade`, or `CONNECT` | Refuses it |
| A method override header or `_method` parameter | Refuses it |
| `Range` or `If-Range` | Drops it, so an echoed key arrives whole |
| `Accept-Encoding: gzip` | Asks for an uncompressed response and refuses a compressed one |

Streaming and server-sent events work over the local HTTP/1.1 connection.

## Postgres

```shell
seal secret --to postgres://app@db.example.com:5432/prod --app checkout < db-password.txt > db.blob
```

Seal only the password. Open a database session and give its `dsn` to your driver:

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

| Rule | Detail |
| --- | --- |
| TLS required | A server that won't do TLS is refused. No plain-text fallback |
| SCRAM-SHA-256 only | MD5 and cleartext logins are refused. Before Postgres 14, set `password_encryption = scram-sha-256` and reset the password |
| Fixed login | Always the sealed user and database, whatever your driver asks for. Startup `options` are dropped |
| Password | Printable ASCII, 16 to 1,024 bytes |
| Refused | Replication connections and fast-path function calls |

The local DSN says `sslmode=disable` because it points at the SDK's tunnel on `127.0.0.1`. Both hops beyond it use TLS. The enclave doesn't read your SQL, so grant the sealed user only what your app needs.

## Redis

```shell
seal secret --to rediss://cache.example.com:6380/0 --app checkout < redis-password.txt > redis.blob
```

Use `rediss://`. Put an ACL user before the host if needed. Pipelining, transactions and pub/sub work. These commands get `NOPERM`, and the connection stays open:

| Kind | Commands |
| --- | --- |
| Login | `AUTH`, `HELLO … AUTH` |
| Other databases | `SELECT` of another database, `SWAPDB`, `MOVE`, `COPY … DB`, `FLUSHALL`, `MIGRATE` |
| Connection changes | `RESET`, `MONITOR`, `SYNC`, `PSYNC`, `REPLCONF`, `CLIENT REPLY OFF` or `SKIP` |
| Administration | `ACL`, `CONFIG`, `MODULE`, `DEBUG`, `SHUTDOWN`, `REPLICAOF`, `FAILOVER`, `CLIENT KILL` or `PAUSE` |

The Redis user's ACL decides everything else, including scripts.

## Certificates

Each release trusts the Mozilla root list plus Amazon RDS's bundle. That covers public APIs, RDS, Aurora, Azure Database for PostgreSQL and Azure Cache for Redis. Google Cloud SQL works only in Google-managed shared CA mode. For a self-signed certificate or private CA, [talk to us](mailto:harvey@seal.zone).

`--pin sha256/<base64>`, up to four times, requires that key in the chain. Pin your provider's CA key, since the server's can change at renewal. Compute it from `ca.pem`, then add `sha256/` in front:

```shell
openssl x509 -in ca.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64
```

## What's not supported

- Keys in the request body or query string.
- Plain `http://`, `redis://` or Postgres without TLS.
- MySQL, MongoDB, S3 and ClickHouse.
- WebSockets and Redis Cluster.
- Private-network databases from Seal's public replica. [Run your own](https://seal.zone/docs/hosting) for those.

Need one? Write to [harvey@seal.zone](mailto:harvey@seal.zone). [Limits](https://seal.zone/docs/limits) lists every size and rate.
