Using Seal
MarkdownAPIs and databases
What Seal can sit in front of today: HTTPS APIs, Postgres and Redis, and what each one allows.
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.
--tohttps://api.example.com/v1url and a stand-in key--torediss://cache.example.com:6380/0dsnAnyone 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.
--headerpicks another header. A few, such asHostandCookie, aren't allowed.--formatsets the value, with{key}exactly once. The default isBearer {key}forAuthorizationand{key}for anything else.
$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.blobPass 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:
blob = seal.seal(key, to="https://api.anthropic.com", apps=["checkout"], header="x-api-key")const blob = await seal.seal(key, { to: "https://api.anthropic.com", apps: ["checkout"], header: "x-api-key" });blob, err := client.Seal(ctx, []byte(key), "https://api.anthropic.com", []string{"checkout"}, seal.WithHeader("x-api-key"))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:
--to, like /v1/v1 and below, by whole segment: /v1/chat, not /v10. Anything else gets a 403--methods GET,POST--deny /v1/organizationPaths 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.
{
"error": "Invalid key sk-proj-7Hq2Lx9Vb9Xc4Ws1",plain text
"echo": "sk\u002dproj\u002d7Hq2Lx9Vb9Xc4Ws1",JSON-escaped
"header": "Bearer%20sk-proj-7Hq2Lx9Vb9Xc4Ws1",URL-encoded
"debug": "c2stcHJvai03SHEyTHg5VmI5WGM0V3Mx",base64
"key_hash": "9f86d081884c7d65…"a hash of the key
}
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#
Host headerUpgrade, or CONNECT_method parameterRange or If-RangeAccept-Encoding: gzipStreaming and server-sent events work over the local HTTP/1.1 connection.
Postgres#
$seal secret --to postgres://[email protected]:5432/prod --app checkout < db-password.txt > db.blobSeal only the password. Open a database session and give its dsn to your driver:
with seal.session(os.environ["DATABASE_BLOB"], kind="database") as db:
conn = psycopg.connect(db.dsn)password_encryption = scram-sha-256 and reset the passwordoptions are droppedThe 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#
$seal secret --to rediss://cache.example.com:6380/0 --app checkout < redis-password.txt > redis.blobUse 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:
AUTH, HELLO … AUTHSELECT of another database, SWAPDB, MOVE, COPY … DB, FLUSHALL, MIGRATERESET, MONITOR, SYNC, PSYNC, REPLCONF, CLIENT REPLY OFF or SKIPACL, CONFIG, MODULE, DEBUG, SHUTDOWN, REPLICAOF, FAILOVER, CLIENT KILL or PAUSEThe 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.
--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:
$openssl x509 -in ca.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64What'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 for those.
Need one? Write to [email protected]. Limits lists every size and rate.