Security Overview

How SikkerKey secures your secrets, where our responsibility ends, and where yours begins.

SikkerKey encrypts, stores, transports, and authenticates your secrets. Once a secret leaves our API, it is in your hands.

This page describes the controls SikkerKey runs and marks the points where responsibility transfers to you. Every claim here describes how SikkerKey works today.

How SikkerKey Secures Your Secrets

Encryption at rest

Every secret value is stored under three layers of encryption, a pattern known as envelope encryption.

  • Per-secret data key. Each secret value is encrypted with its own fresh 256-bit key using AES-256-GCM and a random IV for every write. The secret's identity is bound into the encryption itself, so a value stored for one secret cannot be read as another.
  • Per-project master key. Data keys are themselves encrypted with a key unique to their project. Every project has its own master key, so each project's secrets are cryptographically independent of every other project's.
  • Root key. Project master keys are in turn encrypted with a root key. SikkerKey holds the root key on dedicated, isolated infrastructure, separate from the infrastructure that stores and serves secrets. To read a secret, SikkerKey sends the wrapped project master key to the isolated infrastructure, which returns that one project's key for the lifetime of the request. The root key is only ever available to the isolated infrastructure: it is never transmitted to the secret-serving side, never written to disk, and never logged.

When SikkerKey reads a secret, it obtains that project's key from the isolated infrastructure, uses it to unwrap the data key, returns the plaintext, and clears both keys from memory before the request completes. Plaintext secrets are never cached.

Passwords and recovery codes are hashed with Argon2id at OWASP-recommended parameters. TOTP seeds, webhook signing secrets, and sync credentials are encrypted with AES-256-GCM, each category under its own dedicated key, separate from the keys used for other categories such as file attachments.

Encryption in transit

  • DNSSEC signs the DNS records for our domain, so a validating resolver can confirm that the address it resolves for sikkerkey.com is authentic.
  • TLS 1.2+ is enforced at the edge by Cloudflare. Requests on lower protocol versions are rejected before they reach SikkerKey.
  • HSTS on every production response, so browsers reuse HTTPS for one year after first contact.
  • Strict CSP on every production response with a per-request script nonce. Only scripts from our own build can execute in the dashboard, and our API responses cannot be embedded by other sites.
  • Origin and Referer validation on every state-changing request rejects cookie-authenticated requests whose origin doesn't match the configured frontend hosts. Combined with SameSite=Lax cookies, this prevents cross-site request forgery.
  • Hardening response headers on every production response. Alongside HSTS and CSP, SikkerKey sends X-Content-Type-Options, X-Frame-Options, Referrer-Policy, a restrictive Permissions-Policy (denying device APIs such as camera, microphone, geolocation, and USB), and Cross-Origin-Opener-Policy isolation, so the dashboard cannot be embedded in another site or share a browsing context with one.

Machine authentication

Machines authenticate to SikkerKey with Ed25519 request signatures, not bearer tokens. Every request is signed with the machine's private key, covering the method, URL, timestamp, nonce, and body, so any tampering invalidates it. The machine's private key is generated on the machine during bootstrap and never transmitted to SikkerKey, which stores only the public key.

  • The signature is verified before the nonce is consumed. An invalid signature is rejected before it reaches the nonce store.
  • Each nonce can be used only once.
  • Timestamp window is 5 minutes past, 1 minute forward.
  • Rate limits track the source address and the machine identity together.
  • Query strings are not trusted. The signed payload covers the request path, so unsigned parameters added afterward are refused.
  • Failure responses are generic. Every failure returns Authentication failed; the specific reason is recorded in the audit trail, not the response.

AI agent authentication

AI agents authenticate to SikkerKey with Ed25519 request signatures, the same primitive and replay protection as machines. The signed payload, timestamp window, single-use nonce, and rate-limit handling are identical to the machine flow.

Two things differ by design:

  • Agents and machines authenticate through entirely separate identity systems. An agent's credentials are recognized only for agent operations and a machine's only for machine operations; neither can stand in for the other.
  • Agents have no path to a plaintext secret. Management tools that resemble reads return only metadata, never a value. Tools that accept a new value encrypt it and confirm the change without returning it. Reading a secret's value at runtime is a separate surface, available only to machine identities.

This lets you wire an AI client into your secrets infrastructure for management, code integration, and rotation without exposing any plaintext to the agent. The full breakdown of the scope catalog, project allowlist, audit attribution, temporary-secret delivery, and revocation is at the MCP security model.

Dashboard authentication

  • Argon2id password hashing at OWASP-recommended parameters.
  • Password policy rejects passwords under 12 characters, passwords without at least one digit and one non-alphanumeric character, a curated common-password list, passwords containing your username or email local part, and passwords with fewer than 5 distinct characters.
  • Login lockout by both source address and account, on top of per-account and per-address rate limits, so both targeted and distributed attempts hit a cap.
  • Email verification codes and similar one-time secrets are stored only as keyed hashes, never in the clear.
  • TOTP for optional two-factor authentication. Recovery codes are Argon2id-hashed and single-use. Every TOTP entry path (login, password-reset confirmation, first-time setup) is gated by a per-challenge attempt counter that locks the challenge after five wrong codes.
  • Per-purpose signing keys. Tokens for different purposes (sessions, 2FA challenges, OAuth state, integrations) are signed with independently derived keys.
  • Access tokens have a 15-minute lifetime, and every verification checks that the session is still active, so revoking a session takes effect immediately.
  • Password change or 2FA toggle invalidates every existing session across every device, immediately.
  • Refresh tokens are single-use and rotated on every refresh. If one is ever reused after rotation, SikkerKey revokes every session for the account. The absolute session lifetime is 90 days for customer accounts, 30 days for employee accounts.
  • OAuth login is bound to the originating browser via a secure, HttpOnly cookie set when the flow starts. The callback proceeds only when the state and the cookie agree, so a flow started in one browser cannot be completed in another.
  • Session cookies are HttpOnly, Secure (in production), and SameSite=Lax. Authentication state is not exposed to client-side scripts.
  • Passkey support (WebAuthn). The dashboard supports passkeys for sign-in. A passkey can act as the second factor after a password, or replace the password entirely when the account opts into passwordless mode:
    • The private key never leaves the user's authenticator. SikkerKey stores only the public key and a per-credential counter; there is no shared secret.
    • User verification (PIN, biometric, or whatever the authenticator requires) is required at every registration and sign-in.
    • Each passkey ceremony's challenge is held on our side with a single-use handle in the cookie. A finish attempt consumes the challenge, so the same handle cannot succeed twice.
    • Origins are exact-match. A passkey registered against the dashboard works only on the dashboard.
    • Each assertion's counter must exceed the previous value. A regression causes SikkerKey to reject the sign-in and record a critical audit entry naming the affected passkey.
    • Destructive account actions (renaming or removing a passkey, changing the passkey policy, removing the password, linking or unlinking an OAuth account, disabling 2FA, disabling the IP allowlist, destroying the vault) require a fresh passkey verification immediately beforehand. That verification is single-use, expires within five minutes, and is cleared once the action completes.
    • Passkey sign-in, second-factor verification, step-up, signup, and recovery each carry their own per-address rate limits with escalating lockout.
    • When the account requires a passkey at sign-in, TOTP and recovery codes are refused as completion factors. Only a successful passkey assertion or the dedicated recovery flow can start a session.
    • Recovery uses one of the codes generated at first passkey enrollment. Using a recovery code wipes every passkey on the account, clears the passkey-required and passwordless flags, revokes every other session, and writes a critical audit entry. The user then re-enrolls from a known-good device. A recovery code never restores a lost passkey; it only opens the door to enrolling a new one. The recovery codes are the only self-recovery path on a passkey-required account: if both the authenticator and every recovery code are lost, the account cannot be restored by SikkerKey. This is by design. The alternative would be a side door that defeats the purpose of opting into passkey-only sign-in.
  • Enterprise SSO (SAML 2.0). Organizations can require members to sign in through their own identity provider. SikkerKey validates each assertion end to end: the signature against the configured identity-provider certificate, the audience and recipient, a time window, single-use replay protection, and binding to a sign-in request it actually issued. Accounts are provisioned just in time, only for email domains the organization has verified by DNS. Enforcement is optional per organization; when it is on, password, passkey, and OAuth sign-in are refused for members, with a break-glass path for the owner. The identity-provider configuration is encrypted at rest, and changing it requires a fresh passkey step-up.

Authorization

Machine access to a secret requires all six of the following, checked on every single request:

  1. Valid Ed25519 signature over the request
  2. Machine is approved
  3. Machine is enabled
  4. Vault owner's account is active (not suspended)
  5. Machine is linked to the project the secret belongs to
  6. Machine has an explicit grant for that specific secret

No inheritance, no bulk access by default. Granting a machine access to a project does not grant it access to that project's secrets.

If your vault's IP allowlist is enabled, it gates this path too: every machine read must come from an allowed address, on top of the six checks above.

Optional access policies. You can also bind any secret to an access policy that narrows when and how it is read: time-of-day and day-of-week windows, an IP/CIDR allowlist, read-rate caps, a co-signing requirement where a second machine you nominate must approve the read within a short window, a time-to-live or maximum-read count after which SikkerKey destroys the secret automatically, and automatic rotation after a set number of reads. Ephemeral machines can carry the same kinds of guardrail individually: a per-machine IP allowlist, a country geo-fence that denies any source SikkerKey cannot place in an allowed country, and time windows. These controls are opt-in and layer on top of the checks above; a secret or machine with none configured is governed by those checks alone, and SikkerKey enforces any that are set on every read.

Dashboard access for organization members is enforced on every request against the member's capability template (what they can do) and project scope (which projects it applies to), plus the vault owner's account status. A member can act only within the capabilities their template grants and the projects they're scoped to; a member with no template can sign in but take no action.

Canary tripwires

Plant a canary alongside your real credentials in a project. The canary is indistinguishable from any other secret on the wire. When any machine reads it, SikkerKey freezes the project before the response returns: every subsequent read in that project, from any machine, is refused until the vault owner clears the freeze from the dashboard.

The freeze commits before the response is built. By the time the canary's reader receives a normal-looking response carrying the canary value, the frozen state is already in the database and the canary_triggered and project_freeze audit entries are already written. A second read that arrives in between is refused rather than served.

This limits exposure to a single read. The freeze lands before the response to the triggering read is returned, so the window for further reads is bounded by request latency, not by how fast an operator reacts. A trigger can optionally extend the freeze to every other project in your vault that the offending machine belongs to, so lateral-movement containment is a configuration toggle.

The full mechanics covering arming, trigger configuration, recovery, and the audit event catalog are at Canary Secrets.

Audit trail

Every privileged action is recorded in an append-only audit log. Append-only is enforced by the database itself, not by the application: once written, an entry cannot be edited or removed, and only the automated retention process deletes rows that have aged out under your plan's retention policy.

Audit entries are sanitized of control characters before storage, so a value like a machine name cannot inject misleading content into a downstream log viewer.

See the full audit reference for every action type, its severity, and when it fires.

Tenant isolation

Every project has its own master key. No key material is shared across projects, regardless of which vault owns them, and each project's master key decrypts only that project's data. SikkerKey never decrypts one customer's data during a request scoped to another.

Membership is scoped to a single vault. A member of one organization has no visibility into any other vault, even when the same owner runs both; each member acts only inside the vault they select at sign-in.

How SikkerKey protects its own application secrets

SikkerKey's own application secrets are stored under the same envelope encryption as customer secrets, not in environment variables or config files. SikkerKey's operational credentials live in a dedicated vault on the same production database, encrypted per-project under a master key, which is itself wrapped by the root key.

SikkerKey authenticates to its own vault with a dedicated machine identity, using the same Ed25519 signature model customers use. Every read SikkerKey makes against its own vault is recorded in the audit trail, the same way customer reads are. There is no shortcut path that bypasses the vault: if SikkerKey cannot read its own credentials, it does not start serving traffic.

SikkerKey's own application credentials therefore sit behind the same locks customers use. With the database alone, both customer secrets and SikkerKey's application credentials remain ciphertext.

Defense in depth

  • Isolated webhook delivery. SikkerKey delivers webhooks from a dedicated relay, separate from the infrastructure that stores and serves secrets, so delivery traffic never originates from SikkerKey's core. Before each delivery the relay checks the destination against reserved and internal address ranges that are never a legitimate customer endpoint, then pins the connection to the verified address so it cannot be redirected between the check and the request. Every webhook reaches you from one stable address, 49.13.140.25; allowlist it on your endpoint if you filter inbound traffic.
  • Rate limits cover login, registration, password reset, TOTP verify, email verify, machine auth, bootstrap, enrollment, webhook delivery, and a general API bucket. Failed machine-auth attempts lock out both the source address and the machine after 3 failures in 5 minutes, for 30 minutes.
  • Request size limits are enforced before any handler runs. Requests that do not declare their size up front are rejected.
  • Generic error responses. When an unexpected error occurs, SikkerKey returns Internal server error and records only the error type internally, never the error's own message, which could otherwise contain fragments of the request.
  • Ready before it serves. SikkerKey serves no application traffic until it has established its link to the isolated infrastructure that holds the root key and finished initializing. Any request that arrives before SikkerKey is ready is refused outright. Like everything on this page that touches the root key, this is internal to how SikkerKey runs, included for transparency rather than because it asks anything of you.

Backups

Database backups run daily at 03:00 UTC.

  • A compressed dump of the database is produced.
  • The dump is encrypted with AES-256-GCM under a dedicated backup key, distinct from both the root key and the data keys. The backup key lives inside SikkerKey's own application-secret vault and is unwrapped the same way every other application secret is, through the isolated infrastructure that holds the root key.
  • A SHA-256 checksum is computed over the encrypted bytes.
  • The encrypted blob and checksum are pushed to a separate backup server over HTTPS. Every upload is cryptographically signed, replay-protected, and verified by the backup server, which re-checks the checksum after upload.
  • Every push records its trigger, outcome, size, checksum, and timestamp.

The backup server holds only encrypted blobs. The backup key is not present on it and is not reachable from any configuration file; it can be unwrapped only through the isolated infrastructure that holds the root key.

Hosting & Data Residency

  • Production runs on Hetzner bare-metal servers in Germany. No hyperscaler cloud, no US-hosted infrastructure, no shared virtual machines.
  • Inbound traffic reaches SikkerKey only through a Cloudflare Zero Trust tunnel.
  • Production data at rest stays in the EU. The database in Germany and backups in Denmark sit under German and EU data-protection law (GDPR, BDSG). Stored data is not replicated across borders.
  • In-transit traffic passes through Cloudflare's global edge. Cloudflare terminates TLS at the edge location closest to the requester, which may be outside the EU, then proxies the request through the tunnel to the German origin where SikkerKey processes it.
  • Backup storage runs on a physical server we own outright in Denmark, separate from any cloud provider or third-party datacenter. The backup key is not held on the backup hardware, so the backup blobs alone are unreadable.
  • Owning the backup hardware means restore-from-backup is an immediate action on our side, not a support ticket routed through a cloud provider's SLA.
  • SikkerKey is based in Denmark. The sub-processors required for the product (Cloudflare for TLS termination and edge protection, payments, email delivery) are listed at sikkerkey.com/sub-processors.
  • Customer IP addresses are never sent to a third-party geolocation service. The audit log's country and city columns come from a locally-hosted geolocation dataset; every lookup happens in-process and the IP never leaves SikkerKey's infrastructure.

What We Store vs. Never See

Stored on our sideNever on our side
Encrypted secret ciphertexts and wrapped data keysDecrypted plaintext secrets (decrypted in memory on demand, zeroed after use)
Machine public keys, registered during bootstrapMachine private keys (generated on the customer's machine, never transmitted to us)
WebAuthn credential public keys and per-credential signature countersPasskey private keys (held by the user's authenticator, never transmitted to us)
Your SAML identity provider's configuration (entity ID, sign-in URL, signing certificate), encrypted at restYour identity provider's private signing key (held by your IdP, never sent to us)
Email addresses, usernames, audit events, rate-limit stateThe root key, anywhere on the infrastructure that stores your data (it lives only on separate, isolated infrastructure)
Argon2id hashes of passwords and recovery codesPlaintext passwords or recovery codes
Stripe customer and subscription IDsPayment card numbers (held by Stripe)
Encrypted webhook signing secrets, TOTP seeds, sync credentialsThe root key in any persistent storage anywhere in our infrastructure
SikkerKey's own application secrets, encrypted under the same envelope as customer secretsSikkerKey application secrets in an environment variable or configuration file

Employee Data Access

SikkerKey commits to never building an employee route that reads, creates, modifies, rotates, or individually deletes customer secrets, and never building an employee route that manages, adds, approves, revokes, renames, or bootstraps a customer's machines. This is a standing policy, not a "for now" constraint. It is your data, not ours.

What employees can do

Through the internal operations portal, an employee can:

  • Manage user accounts, subscription plans, tickets, SLAs, and suspension categories.
  • Read a customer's audit log when working a support request. The audit log records what happened; it does not contain secret values, passwords, recovery codes, private keys, or session tokens.
  • See aggregate counts of a customer's projects, secrets, and machines. Names and counts, never values.
  • Delete a user account. This is a destructive cascade: the user's secrets, machines, project memberships, organization invites, and audit history are removed along with the account. It is the only path by which an employee action can remove customer data, and it acts at the account level, not at the individual-secret or individual-machine level.

What no employee route can do

  • Read a customer secret's plaintext. No employee endpoint decrypts customer data, loads a project master key, or obtains any intermediate key material. The only routes that decrypt are the machine-authenticated endpoints, which require a customer-controlled Ed25519 signature.
  • Create, update, rotate, roll back, rename, or individually delete a customer secret. There is no "support write" path.
  • Approve, deny, revoke, rename, bootstrap, or add a customer's machine. Machine lifecycle is exclusively in the hands of the customer. Grants between machines and secrets are likewise untouchable by employees.
  • Impersonate a customer account. The one impersonation feature in the employee portal is hard-fenced to SikkerKey-operated internal vaults only. Every attempt to impersonate a customer account is rejected before any session token is issued, and the rejection sits alongside a permanent audit record. There is no path for an employee to act as a customer.

Why the commitment holds at the cryptographic layer

Even with direct database access, an employee obtains only ciphertext. Every customer secret is encrypted under a per-project master key that is itself wrapped by the root key. The root key never touches the infrastructure an employee could reach: it is held on separate, isolated infrastructure, never written to disk, and never logged. Decrypting customer data requires the root key, and no employee credential, support tool, or internal flow produces it.

We commit to keeping it that way. If a support issue requires a customer's secret value, the customer hands it to us. We do not and will not reach into their vault.

Where Our Responsibility Ends

Three clear transition points. Responsibility transfers at each.

  • The API response leaves SikkerKey. The plaintext bytes are now in your process. What happens to them from that moment on is outside our visibility. This applies whether the caller is your CLI, your application's SDK, a CI/CD runner that bootstrapped a temporary machine identity at the start of its workflow, or any other authenticated client.
  • You add a member to your organization and assign them a capability template. They can then take the actions that template grants, in the projects they're scoped to. If that includes attaching machines, each machine they attach is another authenticated client subject to the first bullet: the API response leaves SikkerKey, the bytes are in its process.
  • A managed-secret agent completes a rotation. The new credential is now live in your database, and SikkerKey does not connect to, monitor, or see anything past that point.

What's On Your Side

  • Your machine's private key file, generated on your machine during bootstrap
  • Your dashboard account credentials, including password and 2FA factor
  • Your passkey authenticators (hardware tokens, platform authenticators, password-manager-synced passkeys) and the recovery codes saved at enrollment. A lost authenticator with no recovery code stored is not recoverable by SikkerKey
  • Who you invite to your vault and what you grant them
  • If you use SSO: your identity provider and its configuration, and the DNS records that verify your SSO email domains
  • Your IP allowlist configuration, if enabled
  • Revoking machines through the dashboard when you decommission them