seal

Reference

Markdown

Limits

Sizes, protocols, rates and timeouts, in one place.

The enclave, the SDKs and the CLI enforce every number here. Going over one usually returns BadRequest when you seal or PolicyDenied in a session; see Errors. Plan allowances aren't enforced yet, so they aren't listed.

Secrets#

HTTPS API, in a header
Length16 to 8,192 bytes
CharactersPrintable ASCII and spaces only
Postgres password
Length16 to 1,024 bytes
CharactersPrintable ASCII and spaces only
Redis password
Length16 to 1,024 bytes
CharactersAnything except a NUL byte

Why 16 bytes: someone who can make the upstream echo chosen text could test guesses against the response masking, and a short secret falls fast. The minimum can refuse an existing short database password, so set a longer one first. Header values can't carry control characters, and printable ASCII keeps Postgres's password normalization independent of the secret's bytes.

What you seal for#

Destination schemes
Limithttps://, postgres:// (or postgresql://), rediss://. http://, redis:// and plaintext Postgres are refused.
Destination URL
LimitNo password, no query string, no fragment. Postgres needs a user and exactly one database.
Default ports
Limit443 for HTTPS, 5432 for Postgres, 6379 for Redis.
Path prefix
LimitAn absolute ASCII path: no %, ?, #, \, . or .. segments, or //. Requests outside it are refused.
Database user and name
Limit1 to 63 visible ASCII characters, no percent-encoding.
Redis database
LimitA number in the path, default 0. Commands for any other database are refused.
Apps per blob
LimitAt least one. A blob never works for an app it doesn't name.
Header
LimitA valid HTTP header name. Not host, content-length, transfer-encoding, connection, cookie, te or upgrade.
Header format
LimitContains {key} exactly once. Printable ASCII and spaces only.
Methods
LimitUppercase HTTP method names.
Pins
LimitUp to 4 per blob.
App and tenant names
Limit1 to 32 characters of a-z, 0-9 and -, not starting or ending with -.
Upstream certificates
LimitMust chain to the Mozilla roots or the AWS RDS bundle. Azure's managed database and cache certificates are in the Mozilla set. See APIs and databases.

Protocols#

HTTP#

The key goes in a header, never the body or query string.
WhyThe enclave can only place it in a header today.
No WebSockets, Upgrade or CONNECT.
WhyA tunnel could carry the key past the enclave's checks.
Requests use a plain path (/v1/…), not a full URL.
WhyThe enclave always sends to the sealed host.
Redirects come back to your app unfollowed.
WhyFollowing one could send the key to another host.
Method-override headers and _method parameters are refused.
WhySome frameworks read them as the real method, around --methods.
Compressed responses are refused. The enclave asks for uncompressed ones.
WhyIt can't scan a compressed body for the key.
Range and If-Range headers are dropped.
WhyA partial response could slip a piece of the key past the masking.
With --methods set, multipart/form-data is refused, and form or JSON bodies are read whole, up to 1 MiB.
WhyThe enclave looks for a _method field.
One stand-in key per connection.
WhyA connection belongs to one session.
Responses stream, server-sent events included. A few bytes are held back until 100 ms of quiet.
WhyThat catches a key split across chunks.

Postgres#

The server must offer TLS and SCRAM-SHA-256. No md5, no plaintext passwords.
WhyThe others would expose the password to the network or the host.
The enclave logs in as the blob's user and database, whatever your client sends.
WhyThe blob decides where the password goes.
Replication and FunctionCall are refused. Startup options are dropped.
WhyThey reach around the checked connection.
One query or prepared statement can be up to 16 MiB.
WhyThe enclave reads it whole before passing it on.
Cancel requests reach only the replica holding the connection.
WhyEach replica keeps its own connections.

Redis#

TLS only (rediss://). Standalone servers, not Redis Cluster.
WhyCluster redirects go back to your client and aren't followed.
AUTH again, HELLO … AUTH, admin commands (ACL, CONFIG, MONITOR, FLUSHALL and others) and other databases are refused.
WhyThey change who you are or reach past the sealed database.
One argument up to 64 MiB, one command up to 96 MiB.
WhyCommands are read whole to check them.

Requests, sessions and timing#

Per replica. Going over a rate limit returns BudgetExceeded.

Open connections
Value1,024 in all, API calls and app traffic together
Connections from one source
Value512 per IPv4 address or IPv6 /64. Apps behind one NAT address share it.
Seals
ValueBursts of 20, then 2 per second, per source
Identity proof tickets (the SDK reuses one for about 2 minutes)
ValueBursts of 20, then 5 per second, per source
Seal and session-start request body
Value1 MiB
Extend, end and signal request body
Value64 KiB
Idle API connection
ValueClosed after 120 seconds
TLS handshake
Value10 seconds
Idle HTTP keep-alive from your app
Value30 seconds
Session token
Value10 minutes
What it means for youLives inside the SDK. Your code never handles it.
Extend
ValueEvery 5 minutes
What it means for youThe SDK proves the app's identity again, automatically.
Grant and settings changes reaching replicas
ValueAbout 30 seconds
What it means for youReplicas recheck live sessions on each change.
Enclave attestation age
ValueAt most 120 seconds
What it means for youThe SDK refuses an enclave with an older hardware report.
Waiting for an unreachable replica
ValueUp to 120 seconds
What it means for youThe SDK tries the other replicas, then raises Unavailable.
Disabling an AWS, GitHub or Kubernetes identity at its provider
ValueAbout 5 minutes
What it means for youA copied credential stops at the next extend.
Disabling an Azure or GCP identity at its provider
ValueUntil its cached token expires
What it means for youUp to 24 hours for an Azure managed identity. Remove the grant in Seal to cut it within about 30 seconds.

Replicas and telemetry#

Seal-hosted replicas per tenant
ValueOne, in one Azure region
Seal-hosted idle stop
ValueAfter 15 minutes with no authenticated traffic. Replicas you run never stop when idle.
Cold start
ValueAbout a minute. The SDK waits up to 120 seconds, so the first call after a quiet spell is slow.
Exit timer on your own replica
ValueSEAL_EXIT_AFTER_SECS, 60 to 604,800 seconds (one week)
Telemetry batch interval
ValueAt most every 5 seconds
Telemetry buffer
Value10,000 unsent entries per replica. Past that, request entries are dropped first, and each drop is recorded.

See Where the enclave runs and Telemetry.

Tenants and admins#

Tenant id
Limit1 to 32 characters of a-z, 0-9 and -. It can't be changed later.
Threshold
Limit1 up to the number of admin keys
Pending window
Limit0 to 24 hours. Shortening it waits out the current window.
Freshness window
Limit5 minutes to 24 hours. Default 24 hours.
Signal codes
Limit1 to 64 characters of a-z, 0-9, - and .
Release signatures
Limit2 of 3 release keys. See Releases and the log.

Keep at least two admin keys. If you lose your only one, nobody can ever change the tenant again.

Freshness and failing closed#

A replica acts only on a tenant state with a current freshness statement, so nobody can replay an old state for longer than the window.

No fresh state for longer than the freshness window
What happensEvery seal and session fails with Unavailable until the replica gets one.
Key guardian unreachable when a replica boots
What happensThe replica gets no keys and serves nothing.
Key guardian unreachable while a replica runs
What happensIt keeps its tenant keys for 15 minutes, then starts no new sessions until it reaches the guardian.

An outage stops your keys from working. It never makes them readable. A shorter freshness window limits replays, and also how long a replica rides out an outage.