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.

TierHow the key is heldWhere encryption runs
FreeEach 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 wrapped key shares. Once unwrapped, rypt caches it for five minutes and scrubs its copy within about ten. Copies inside the Cloud KMS client are freed, not zeroed.In rypt, with AES-256-GCM

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, and it is never written to rypt's database or logs. Cloudflare terminates TLS in front of rypt, so every request, plaintext and API key included, passes through Cloudflare's network. 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 wrap and unwrap, are zeroed as soon as the response is built. The base64 copy in rypt's response, and copies inside the HTTP and Cloud KMS client libraries, are freed at the end of the request, not zeroed. A 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 Cloud Run's own request log also records each request's URL, status, user agent and connecting address, which is a Cloudflare edge. Your own IP is recorded in the audit row.
  • Audit rows record sizes, not content. The bytes field is the length of the operation's input.
  • API keys are stored as hashes. Each API key is kept as an Argon2id hash of the whole token, plus its eight-character lookup prefix in clear. A new API key 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 keys run the cryptography. Only an API key (Authorization: Bearer ry_...) can encrypt, decrypt, wrap, unwrap or rewrap. A dashboard session cannot, so the dashboard never handles your plaintext.
  • An API key is account-wide. Besides the five operations, it can create, rotate and delete every key in the account and read its audit logs and usage. There are no per-key or read-only API keys, so treat each one as full control of your keys.
  • Dashboard sessions manage API keys. Dashboard sign-in runs through Auth0. Through the API, only a signed-in dashboard session can issue or revoke an API key, and an API key cannot create another one. A dashboard session can also create, rotate and delete every key, so protect that sign-in as your account's root credential.
  • Revocation is immediate. A revoked API key is refused on its next request.
  • Every audited call is traceable. Each response carries an x-request-id header, every error body repeats it, and every audited operation records it in its row.
  • The network path is narrow. Requests reach api.rypt.dev through Cloudflare, and rypt refuses requests that bypass it. Browser requests are accepted from dashboard.rypt.dev only, and never with cookies. The database has no public IP address.

Audit log

Append-only, and read per key

Each create, rotate and delete, and each encrypt, decrypt, wrap, unwrap and rewrap on a key in your account, writes an audit row, whether it succeeds or fails. Requests refused before they reach a key are not recorded, for example a bad or revoked API key, the rate limit, a malformed or oversized body, or an unknown or deleted key id. A failed create is recorded without a key, so it does not appear in any key's log.

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, rotate or delete, check the key before you retry.

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, the length of the input, and which API key or dashboard user acted. Read a key's log with GET /v1/keys/{id}/audit, including after the key is deleted 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}/rotate or rotate from the dashboard. The new version becomes the one new ciphertext is made under.
  • A Software key keeps three versions and a Hardware key keeps two. When a rotation takes a key past that count, its oldest version is scheduled for destruction and stops decrypting. To keep old ciphertext readable, rewrap it onto the current version before that happens.
  • The free key keeps one version. Rotating it makes everything encrypted under the previous version undecryptable as soon as the rotation succeeds. If you plan to rotate a key, use a Software or Hardware key.
  • Deletion cannot be undone from your side. Deleting a key schedules every version for destruction. Every call on the key answers 404 from that moment, except its audit log, which stays readable.
  • 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. wrap will 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 to us-central1. The usage log lines are in BigQuery's US multi-region, logs are kept in Google Cloud Logging, and dashboard sign-in identities are held in Auth0's US region. 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 key.