Security
Where your keys live, and who sees what
Where keys are held, who handles your plaintext on the way, what is stored and what never is, and the claims rypt does not make.
Keys
Google Cloud KMS, in us-central1
Keys are held in Google Cloud, in a project that x6c LLC operates, in the us-central1 region. Software and Hardware keys are Cloud KMS keys. Free and Extra wrapped keys are stored wrapped in rypt's Postgres database (Cloud SQL, with no public IP), under one root key held in Cloud KMS at the HSM protection level. Your account never touches Google Cloud. There are no service accounts, IAM roles or key rings for you to manage.
| Tier | How the key is held | Where encryption runs |
|---|---|---|
| Free | Each key version gets its own random 256-bit data key. It is stored only in wrapped form, in rypt's database and that database's backups, encrypted under one HSM-protected Cloud KMS root key that every Free and Extra wrapped key shares. Once unwrapped, rypt caches it for five minutes and removes it within about ten, best effort, zeroing the cache's copy as it does. Copies inside the Cloud KMS client are freed, not zeroed. | In rypt, with AES-256-GCM |
| Extra wrapped | Held the same way as a Free key: its own data key for each version, wrapped under the same HSM-protected root key. | In rypt, with AES-256-GCM |
| Software | Each key is its own Cloud KMS key at the SOFTWARE protection level, in a key ring for your account. | In Cloud KMS |
| Hardware | Each key is its own Cloud KMS key at the HSM protection level, in a key ring for your account. | In Cloud KMS, on a hardware security module |
A key's tier is fixed at creation. Moving data to another tier means decrypting it and encrypting it again under a new key.
Your data
What rypt sees, and what it keeps
- Plaintext passes through and is not stored. It arrives over HTTPS on encrypt and leaves the same way on decrypt. It is not stored in rypt's database, and the Service is built so that it is not written to its logs. TLS ends at Google's global load balancer in front of rypt, so the load balancer handles every request and response decrypted, including your plaintext, data keys and API token. It answers plain HTTP with a redirect to HTTPS, but a client that starts on
http://has already sent its request unencrypted by then, so usehttps://only. For Software and Hardware keys the plaintext also goes to Cloud KMS, where the encryption runs. - Decoded copies are zeroed. Decoded plaintext, and the data keys you send or receive through
wrapandunwrap, are zeroed as soon as the response is built. The raw request body, the base64 copy in rypt's response, and copies inside the HTTP server, the JSON parser and the Cloud KMS client are freed, not zeroed. Theaadyou send is not treated as secret and is not zeroed. A Free or Extra wrapped key's own data key is cached as described above. - Logs record requests, not their contents. The request log records the method, URL, request id, status and latency, with no headers and no bodies. Each cryptographic operation also logs one usage line with your account id, the operation, the result, the byte count, and the request's method, URL (which names the key) and request id. Those usage lines are copied to a BigQuery dataset in the US multi-region and kept indefinitely. Google's load balancer also logs every request, with the address it came from, and Google Cloud Run's own request log records each request's URL, status and user agent. Your IP is also recorded in the audit row.
- Audit rows record sizes, not content. The
bytesfield is the length of the operation's input. - API tokens are stored as hashes. Each API token is kept as an Argon2id hash of the whole token, plus its eight-character lookup prefix in clear. A new API token is shown once and cannot be recovered.
- This is not zero-knowledge. Your plaintext reaches rypt so it can be encrypted, and rypt can use the keys that decrypt it. If you need keys that only you control, use a KMS in your own cloud account.
Credentials
Two credentials, kept apart
- API tokens run the cryptography. Only an API token (
Authorization: Bearer ry_...) can encrypt, decrypt, wrap, unwrap or rewrap. A dashboard session cannot, so the dashboard never handles your plaintext. - An API token is held to one container. It reaches only that container's keys, and does only the actions it was given, out of twelve:
encrypt,decrypt,wrap,unwrap,rewrap,rotate,create,rename,delete,restore,retireandread. A new API token has none unless you give it some.decryptandunwrapaccept each other's output, so giving either one is in effect giving both. You can also hold it to a list of keys, which rules outcreate, and to a firewall of the networks it may be used from. Withoutreadit cannot even list or read the keys it reaches, their audit logs or usage. Access control has the details. - Dashboard sessions manage API tokens. Dashboard sign-in runs through Auth0. Through the API, only a signed-in dashboard session can issue, restrict or revoke an API token, and an API token cannot create another one or change its own restrictions. A dashboard session is not limited by actions and can create, rotate and delete every key, so protect that sign-in as your account's root credential.
- Revocation is immediate. A revoked API token is refused on its next request.
- Every audited call is traceable. Each response carries an
x-request-idheader, every error body repeats it, and every audited operation records it in its row. - The network path is narrow. Requests reach
api.rypt.devthrough Google's load balancer, and Cloud Run, where rypt runs, takes requests from the internet only through it. The load balancer passes on the address you connected from, which rypt checks against an API token's firewall and records in the audit row. The API accepts browser requests fromdashboard.rypt.devonly, and those requests carry no cookies. The database has no public IP address.
Audit log
Append-only, and read per key
Each create, rename, rotate, delete, restore and delete now, each retirement or restore of a key version, each change to how many versions a key keeps, and each encrypt, decrypt, wrap, unwrap and rewrap on a key in your account, writes an audit row, whether it succeeds or fails. That includes a call refused with 409 key_pending_deletion because the key is pending deletion, and one refused by an API token's key list or actions, with 403 key_not_allowed or 403 action_not_allowed, when the key is in that API token's container and not destroyed. Other requests refused before they reach a key are not recorded, for example a bad or revoked API token, a firewall refusal, the rate limit, malformed JSON or a body over 128 KiB, or an unknown or destroyed key id. Nor is a delete of a key already pending deletion, a rename to the name it has, the retirement of a version already retiring, or setting the number of versions a key already keeps, which change nothing. A failed create, a refused one included, is recorded without a key, so it does not appear in any key's log. When rypt destroys a key because its 7 days of pending deletion have passed, or a retired version because its 7 days have passed, it writes a row for that too.
A database trigger rejects any update or delete of an audit row. That stops rypt's own code from changing the log. It does not protect the log from x6c LLC, which operates the database. If a row cannot be written, the call returns an error instead of reporting success. The operation may already have taken effect by then, so after an error on a create, rename, rotate, delete, restore or delete now, check the key before you retry. A call on a key that an API token's key list or actions refuse works differently: its 403 is sent first and its row is written after, and a row that cannot be written is lost.
A row records the time, the operation, the key, the key version when known, the result and any error code, the request id, the source IP, which API token or dashboard user acted, and, for the five cryptographic operations, the length of the input and the time spent in the key-management backend, each when known. The rows rypt writes when it destroys a key after its 7 days of pending deletion, or a retired version after its 7 days, have no source IP, API token or user, because no request caused them, and their request id is one rypt made. Read a key's log with GET /v1/keys/{id}/audit, including after the key is destroyed if you kept its id, or on the key's page in the dashboard. There is no bulk export or account-wide audit view today.
Rotation
Rotation and deletion are yours to trigger
- Rotation is manual. Call
POST /v1/keys/{id}/rotateor rotate from the dashboard. The new version becomes the one new ciphertext is made under. - A paid key keeps every version until you retire it. An Extra wrapped, Software or Hardware key keeps every version it is rotated to, and each keeps decrypting until you retire it, by hand or by setting the key to keep at most a number of versions, so that each rotation retires the oldest beyond that number. A retired version keeps decrypting for 7 days, and then rypt destroys it. Within those 7 days you can restore it, with
POST /v1/keys/{id}/versions/{version}/restoreor in the dashboard, or rewrap what you need onto the current version. Each tier's price includes some versions, and from December 1, 2026 each version beyond them is billed, a retiring one included until it is destroyed, as Section 7 of the terms of service describes. - A Free key keeps one version. Every rotation retires the previous one in the same way, and a Free key's retired version cannot be restored, so rewrap everything encrypted under it within its 7 days.
- Deletion waits 7 days. Deleting a key, with
DELETE /v1/keys/{id}or in the dashboard, puts it in pending deletion. From that moment every operation on it,decryptandunwrapincluded, and every rotation, is refused with409 key_pending_deletion. For 7 days you can restore it withPOST /v1/keys/{id}/restoreor in the dashboard. Restoring is free, but a paid key you restore is then billed for the days it was pending deletion too, apart from any in a month that had already ended when you restored it, as Section 7 of the terms of service describes. After the 7 days rypt destroys it: every version is scheduled for destruction, and every call on the key answers404, except its audit log, which stays readable. That happens after the 7 days end, not exactly when they do, and once they end the key can no longer be restored, even while it is still listed as pending deletion:POST /v1/keys/{id}/restoreanswers409 restore_window_closed. - Delete now is final. Delete now, in the dashboard, destroys a key at once in the same way, whether or not it is pending deletion, for a key you think has been exposed. It cannot be undone. If it fails part-way, the key is left in pending deletion and can no longer be restored, and it is destroyed when you try again or later by rypt. Only a signed-in dashboard session can delete a key now. An API token cannot: a leaked API token given
deletecan still delete keys, in a way you can undo for 7 days, one givenrotatecan rotate them, which you cannot undo, and one givenretirecan retire their old versions, which you can restore for 7 days on a paid key. - Destruction is as final as the backups. A destroyed data key of a Free or Extra wrapped key can survive, still wrapped, in database backups until they expire.
Not claimed
What rypt does not claim
- No compliance guarantee. The audit log is evidence. Auditors draw the conclusions.
- No customer-managed keys. Every key is created and held by rypt, and you cannot import your own key material into one.
wrapwill wrap a data key you generate, and that data key stays yours. - No region guarantee beyond us-central1. Cloud KMS keys, the API service and its database run in
us-central1, and your usage counts live in that database. Database backups use Cloud SQL's default backup location, which is not pinned tous-central1, and TLS ends at a global load balancer, which is not pinned there either. The usage log lines are in BigQuery's US multi-region, logs are kept in Google Cloud Logging, dashboard sign-in identities are held in Auth0's US region, and Stripe holds the billing details of any account that sets up billing, and its paid keys' usage counts. There is no multi-region option. - Symmetric keys only. Every key is AES-256 for encryption and decryption. There are no signing or asymmetric keys.
Contact
Report a security issue
Email support@rypt.dev. Include the x-request-id from any response involved, and never send an API token.