SanctiKey

Security model

For your security reviewer

We publish our trust topology. All of it.

Most vendors describe their security in adjectives. This page describes ours in boundaries and controls: what component can reach what, what data lives where, how every operation is gated. And, because honesty means saying it, what we haven’t solved yet. We maintain an internal catalog of 97 numbered controls for the product plane where your keys live (SC-001 to SC-097), of which 91 are active and six are retired, struck out rather than quietly dropped when the architecture moved past them. The billing plane carries its own separate catalog of 14. The excerpts below cite the product catalog directly, by number, so you can hold a specific claim to a specific control.

1. The isolation boundary is an AWS account

Each customer environment is provisioned into a dedicated AWS account. Keys, audit logs, API quotas, identity pools: everything you touch lives inside that account. There is no shared database with a tenant_id column, because there is no shared database.

The platform itself is split into planes with one-way trust: the public edge (this site and billing) writes billing facts to a ledger; the provisioning engine independently verifies those facts against Stripe before it acts. A full compromise of our public website cannot fabricate a paid customer, reach the provisioning engine, or touch any customer account.

2. Key material is non-extractable, including from us

Keys are generated inside AWS KMS using FIPS 140-3 Level 3 validated cryptographic modules, with key material flagged non-extractable, created multi-region-capable, and rotation enforced where the key type supports it. Sign and decrypt operations execute inside the module; the API returns outputs, never key bytes. Multi-region capable is not the same as replicated: a key is born able to live in a second region, and actually placing it there is an add-on you pay for, which we say here rather than in a footnote.

To be precise for a security review: the FIPS 140-3 Level 3 boundary is the AWS KMS HSM (CMVP certificate #4884) that generates and stores your private key material, reached over FIPS-validated AWS endpoints. Key material is created in that boundary and never leaves it; that scope is the entire basis of the guarantee, not a platform-wide certificate.

  • SC-004 + SC-053Before any operation runs, two independent checks confirm the key is yours, exists, is enabled, and matches the requested use. Only then is the cryptographic module called.
  • SC-054Moving encrypted data onto a different key checks that destination key too, so data can never be re-encrypted out to a key you don’t own.
  • SC-055Only valid algorithm and key-type combinations are accepted. There is no way to request a weakened or mismatched operation.
  • SC-062For key types that support automatic rotation, rotation is switched on the moment the key is created. If it can’t be, the key is deleted and creation fails, so a non-rotating key can never quietly exist.
  • SC-031 + SC-004Even a fully compromised application function cannot disable or delete your keys: its permission ceiling explicitly forbids those actions.

Deletion is a lifecycle, not a button, and we treat every way it can silently stall as a thing worth waking up for. A key you asked us to destroy that never actually reaches destroyed answers “pending deletion” to every reader, fails nothing, and keeps billing. So a daily reconciler sweeps for keys stuck mid-deletion, for keys whose promised destruction date has passed, and for state that disagrees with what the module reports, with an alarm bound to each. The reconciler’s own heartbeat alarms on absence, because a backstop that quietly stops running is the one failure a backstop can’t report on itself.

We do not operate a “support backdoor.” If you lose access to your keys and have not configured escrow, we cannot recover them. We consider that a feature, and we say so before you buy.

3. Certificates: a real CA, and revocation that actually publishes

The certificate authority is built on the same seam as everything else: the CA private key is an ordinary customer key that never leaves the module. Our code assembles and parses the certificate structures, and every signature is produced by the module, so there is no in-application representation of a CA key to steal.

  • SC-091The CA private key has no form our code can hold. Certificate and revocation-list signatures route to the module; a compromised issuance function can request a signature the key policy already permits, and nothing else.
  • SC-092What goes into a leaf certificate comes from server policy intersected with your request, never copied out of the CSR. Constraints, key usage, subject, and serial are ours to set, so a crafted CSR cannot mint a rogue intermediate.
  • SC-093A CSR is proved before it is honoured: the embedded self-signature is verified, so you cannot obtain a certificate over a public key you do not control.
  • SC-094 + SC-097Revocation ships with issuance. Revoking a certificate regenerates and republishes that CA’s revocation list, and a public OCSP responder answers signed status in real time. Both are public endpoints, because a relying party checking your certificate has no account with us and never will.
  • SC-095 + SC-096Every designation, issuance, import, and revocation writes an audit record, and validation failures come back as sanitized per-field errors that never echo your input or our rules.

The boundary, stated where we sell it hardest: renewal automation is your pipeline’s job. Signing, revocation, and revocation truth are ours. There is no ACME, SCEP, EST, or enrolment agent, and the trust is private trust. See the known limits below for what that rules out.

4. Sign-in is hardened against entire attack classes

  • SC-007Two-factor sign-in uses authenticator-app codes only, never SMS, so SIM-swap attacks simply don’t apply. Accounts are created by your admin, not open registration, and password recovery can’t be used to skip the second factor.
  • SC-009Sign-ins using passwords known from public data breaches are blocked outright, and unusual sign-in patterns trigger extra challenges.
  • SC-038 + SC-039Sign-in tokens are accepted only in the exact cryptographic form we issue. Forged or downgraded tokens are rejected before their contents are even read, and a token issued for one purpose can’t stand in for another.
  • SC-042Permission changes take effect on the very next request. Revoking an API key or narrowing access never waits for a cache to expire, because authorization is never cached.
  • SC-002 + SC-003An API key can never carry more power than the person who created it, and your admin can cap or shut off all programmatic access at once instead of hunting down keys one by one.
  • SC-005 + SC-052Administrative privileges are stripped before a token is ever signed, so a misconfigured app can’t hand out rights a user doesn’t have. A user in no permission tier gets a token carrying no API scopes at all, which is the fail-closed direction.
  • SC-066When you connect your own identity provider over OIDC, the client secret you hand us goes straight into the secret store and onward to the provider configuration. It is never written to a database, a log, or a response, and it is not readable back out of the API you submitted it to.

You can bring your own identity provider over SAML or OIDC, which introduces its own questions, so here is how we answered them. A person can hold more than one permission tier, and the tiers combine as a union: holding another tier can only ever add permissions, never silently remove one, which is what makes granting a second tier a safe thing for your admin to do. Group names that came from your directory are treated as data compartments, not as tiers, so nothing your directory happens to be called can grant authority here. And a federated person who has been provisioned but not yet granted a tier signs in to a token with no API scopes; the console detects exactly that and sends them back with the instruction that fixes it, rather than rendering a dashboard where every action fails. We rejected auto-granting a default tier: it would hand key authority to any identity in your directory that found the sign-in URL.

5. Payment data: PCI SAQ-A, by construction

Checkout and billing management are hosted by Stripe, on Stripe’s pages, on Stripe’s infrastructure. Your card number and CVV are entered into Stripe’s UI and never transit or rest on a SanctiKey server. We store only opaque Stripe identifiers (cus_…, sub_…), useless to an attacker as payment instruments.

Billing events arrive from Stripe over a cryptographically signed webhook. We verify the signature on the raw payload before parsing a single byte, reject anything older than five minutes, and process every event exactly once. Our signup API actively rejects any request containing card-like fields; accepting card data anywhere is a tested failure condition.

6. The public edge is treated as hostile territory

Two different front doors, and it matters which is which. The web surfaces, this site and the console, sit behind a CDN with a WAF in front of it. The crypto API is not behind that CDN: it answers on its own regional hostname with a regional WAF bound directly to the gateway, carrying managed attack-pattern rule sets, a geographic allowlist, per-IP rate ceilings, and persistent auto-blocking for addresses that keep pushing. We used to inject a secret header at the CDN so the API could refuse anything that arrived another way; that control was retired when the API moved onto its own hostname and the WAF moved onto the gateway itself, and we have struck it from the catalog rather than leave it decorating this page. The billing plane, which still sits behind the CDN, does still verify that header.

  • SC-071 to SC-073Security headers go out on every response, the leg between our CDN and its origin is encrypted, and every hostname we publish floors at TLS 1.2. Web and API surfaces both negotiate TLS 1.3. The web surfaces also negotiate a post-quantum hybrid key agreement and the API does not yet, which is a limit we state below rather than average away.
  • SC-006 + SC-043The API gateway is the hardened front door: a regional firewall with managed attack-pattern rules and per-IP rate ceilings sits directly on it, and requests without credentials are rejected by the gateway itself before any application code runs.
  • SC-080Every request is checked against a strict schema twice, once at the edge and once in the application. Unknown fields and out-of-range values are refused at both.
  • SC-068 + SC-082Abuse detection identifies the real client address in a way that can’t be spoofed, and it won’t ban an entire office because one person behind a shared connection misbehaved.
  • SC-078Cryptographic endpoints carry their own tighter rate ceilings, separate from the rest of the API.

7. Audit integrity is monitored, not assumed

  • SC-029Every database change is recorded with both its before and after state in a tamper-evident stream. A deletion or a status change can’t vanish from history.
  • SC-019If an audit record ever fails to write, that failure itself raises an alarm. A gap in the trail is something we get paged about.
  • SC-079 + SC-061Logs capture diagnostic metadata only, never request contents, tokens, or credentials, and error messages are fixed sanitized text. No stack traces, no internal identifiers, ever.
  • SC-013 + SC-028Data stores can be restored to any point in time and carry two independent layers of deletion protection, because either layer alone can be bypassed by mistake.

8. How changes reach production

The pipeline is part of the attack surface, so it carries the same discipline as the product:

  • SC-084The pipeline holds no stored cloud passwords. Every deployment uses short-lived credentials issued for that one job. There is nothing to steal.
  • SC-085 + SC-032Reading and changing infrastructure are two separate identities, and the one that can change things only works from the main branch. A malicious pull request can look, not touch.
  • SC-030 + SC-031Everything that runs does so under an enforced permission ceiling, and the pipeline is structurally unable to create anything without one. A compromised component stays in its own lane.
  • SC-086 to SC-088Automated scanners gate the build across four categories: infrastructure misconfiguration, insecure code patterns, dependencies with known vulnerabilities, and accidentally committed secrets. Infrastructure scanning, static analysis, and both dependency audits run in CI, where they cannot be skipped; secret scanning and a second infrastructure scanner also run as commit hooks.
  • SC-090Production changes are gated on a human, whether that is the pipeline’s protected-environment approval or the operator running the change by hand. There is no path where infrastructure moves because a job decided it should.

Two gates worth naming because they exist for reasons we learned the hard way. Dependency waivers expire and, more to the point, they carry a premise the gate re-derives from the repository on every run. A waiver that says “this code path is unreachable” fails the build the moment the code makes it reachable, because a gate that checks only an expiry date launders a real finding into a passing build, which is worse than no gate. And the build refuses to publish a shared dependency layer whose compiled artifacts are not built for the processor architecture the functions actually run on, after exactly that mismatch took our own authentication down for about half an hour. Zero customers were affected; we added the check rather than the resolution to be more careful.

9. What constrains us

Everything above is about what the product does. This is about what we can do, which for a key custody product is the question you actually came to ask. “We can’t reach your keys” is worth reading only if the page also says what holds our own hands and our own automation. These controls live in the platform’s operations catalogs rather than the product one, so they carry no SC number here.

  • Administrative access is short, and re-proved by something a fake page can’t relay. The most privileged administrative session on the platform lasts one hour, the shortest of any role we hold, and it ends by expiring rather than by being extended. Getting it back means signing in again, every time, with no remembered devices and no trusted browsers. That sign-in, the administrative path into the accounts we run the platform in, requires a factor cryptographically bound to the origin it is signing in to: a convincing replica of our login page has nothing it can relay, and a notification a tired person could approve isn’t accepted at all. A hardware-backed security key is a required enrolment for every operator, and text message, email, and authenticator-app codes can’t be enrolled in the first place.
  • Our automation runs under a ceiling it can’t raise. Every function that touches key material is created with a hard permission ceiling attached, the pipeline is structurally unable to create one without it, and nothing a function’s own policy grants can exceed it. That isn’t decorative. Three times this month a permission we deliberately granted a function was denied anyway, because the ceiling above it didn’t allow it, and the deploy failed instead of quietly widening. We would rather show you a control that has refused us than one that has never been tested.
  • Nobody gets to be the root credential. Organization-wide policy denies the root credential of every account under it, ours and yours alike, and no account can be pulled out of the organization. These bind us at exactly the same level they bind anyone who steals an operator’s session, which is the only kind of guardrail worth having.
  • Software reaches your account by digest, from a store that can’t be rewritten. Published versions land in a write-once store where an object can’t be altered after it is written, by anyone, including us. The deploy recomputes the content hash and compares it before it applies anything, and a mismatch aborts rather than retries, because a digest that doesn’t match means a tampered or truncated artifact and there is no version of that worth deploying.
  • One narrow path reaches a customer account, and it announces itself. Account creation and placement run through a single piece of automation that can enter only the accounts it created, never the platform’s own. Every use of it raises an alert, one per action, so an unexplained one is an incident by definition rather than a line in a log nobody reads.
  • Mail about you is rendered, not written. Exactly one system on the platform is allowed to send email, and it refuses any caller-supplied subject, body, or HTML. Everything it sends renders from a fixed template registry, so neither an operator nor a compromised component can compose a message to you. Your address is encrypted at rest, reduced to a hash anywhere it appears in a metric, and kept out of logs and event payloads entirely.
  • Personal data has a destruction date, and an honest one. When a cancelled account passes its export window, the encrypted contact fields are removed and the record is tombstoned in a single transaction with the fact that records the removal. What survives is the billing history, which carries identifiers rather than people. Point-in-time backups then age out on their own 35-day clock, so the true worst case is the retention window plus that, which we’d rather write down than round to zero.

10. The billing plane, demonstrated

The plane you used to reach this page is the public edge: signup, billing, and the portal. It writes billing facts a provisioning engine independently re-verifies against Stripe. Here are its three flows end to end; a compromise of any of them cannot fabricate a paid customer or touch a key.

Flow 1

Signup and checkout

You choose how many keys you want and enter an email. Everything about payment happens on Stripe, on Stripe pages. Card data never touches SanctiKey; that is what keeps us PCI SAQ-A by construction.

01

Your browser

Key count, email, and terms acceptance. Nothing else is accepted.

closed schema: unknown and card-like fields rejected
02

Edge + API gateway

WAF, rate limits, schema enforced before any code runs

validated a second time in the application
03

Checkout service

Creates a Stripe-hosted Checkout Session server-side

only a checkout URL comes back
04

Stripe

Card entry on Stripe’s origin, never ours

  • Four fields, two gates. The gateway accepts a key count bounded to 1 through 50, an email matched against a pattern and capped at 320 characters, an explicit terms acceptance that has to be the literal true, and a dated terms version. Anything else, including anything card-shaped, is rejected before a function runs. The application then validates the same contract a second time from one source of truth.
  • Payment on Stripe, not here. Your browser is redirected to Stripe. Card number and CVV are entered into Stripe UI on Stripe infrastructure. The authoritative outcome arrives later via signed webhook (Flow 2), never via the browser redirect.

Flow 2

Billing events, verified before parsed

Stripe tells us what actually happened (payment succeeded, subscription created, subscription cancelled) through webhooks. We treat every webhook as hostile until its signature proves otherwise.

01

Stripe

Signed event delivery, at-least-once

HMAC verified over the raw bytes before any parsing
02

Webhook receiver

Constant-time signature check, 5-minute replay bound

queued, deduplicated on the event id
03

Event queue

Duplicates become permanent no-ops

allowlisted lifecycle fields only
04

Billing ledger

Idempotent write per event; advisory until independently re-verified

  • Verify before parse. The signature is an HMAC over the raw bytes Stripe sent. We verify it with a constant-time comparison and reject anything older than five minutes before interpreting a single byte. A forged payload is never parsed.
  • Allowlisted extraction. The processor extracts a fixed allowlist of lifecycle fields: what happened, subscription status, period end, key and capacity counts, and the terms acceptance record. Card data, addresses, and full payloads are structurally excluded from what gets stored.
  • Advisory, independently verified. These writes are advisory: before the provisioning engine vends anything, it independently re-verifies the subscription against Stripe. A compromise of this public plane cannot fabricate a paid customer.

Flow 3

The customer portal

Once you are signed in, the portal shows your subscription and provisioning status. Who you are is decided by your verified token, never by anything your browser sends in a request.

01

Your browser

Bearer token from sign-in

token signature + edge origin header verified at the gateway
02

API gateway

Requests that bypass the edge fail at zero compute cost

identity taken from verified claims only
03

Portal service

Client-supplied customer identifiers are never read

server-side lookup, redacted at the source
04

Billing ledger

Your projection: lifecycle, entitlements, provisioning state

  • Identity is server-side. The portal derives the customer exclusively from the verified token subject. Requesting someone else’s data is not a blocked action, it is an impossible one: there is nowhere to supply another identity.
  • Redacted by the source. The ledger returns a projection that is already redacted: opaque customer id, lifecycle, entitlements, provisioning state. Billing management itself (upgrade, cancel, invoices) opens on Stripe’s hosted portal.

11. What we cannot see

  • Private keys: no extraction path exists, for anyone
  • Card data: lives at Stripe; we hold tokens only
  • Your plaintext: operations run in-module; bodies are never logged
  • Other tenants from your account: there are none

12. Known limits (the part most vendors omit)

  • We run on AWS. Our isolation model inherits AWS’s account boundary. If you require sovereignty from US hyperscalers entirely, we are not the right product today.
  • FIPS 140-3 applies to the modules, not to every line of our application code. We follow the discipline platform-wide, but the certificate belongs to the HSMs.
  • The AWS account is SanctiKey-owned. We operate it inside our AWS Organization under guardrail policies; you are its only tenant. Exclusive tenancy is architectural. And there is no lock-in: the Keyout Right, a published-fee account hand-off available to every valid, paid-up account, is yours alone to invoke, we never initiate it, so you can take the account and your keys with you when you choose to leave. What conveys is bounded and written down: the account, the keys, and the audit table with its entries. Our operational records, including how we replicate and where, do not come with you. Where that boundary would have cost you something real it doesn’t: revocation facts are folded into the audit record that conveys before the operational ones are pruned, so a certificate you revoked stays revoked and you can republish a truthful revocation list from what you hold.
  • The control catalog is self-attested. The controls cited on this page are documented and tested by us, not yet certified by a third party. We’d rather show you a numbered, specific catalog than a vague badge.
  • The API isn’t negotiating post-quantum key agreement yet. Our web surfaces do, and the API already negotiates TLS 1.3, but it’s still using a classical key exchange because we haven’t set the policy that would give it a hybrid one. AWS publishes that policy for the endpoint type we run, so this is our configuration lagging, not a ceiling we’re stuck under. The surface carrying your signatures is the one we most want it on, which is why it’s written here instead of waiting quietly in a backlog.
  • Our CA is for private trust.Firmware signing, device identity, internal service certificates, mTLS: fully served. Anything that needs to chain to a root already sitting in a browser or operating system trust store is deliberately not something we provide, and no configuration changes that. Matter is the exception people expect to find in this bullet and it is not one: its trust store is the Alliance ledger rather than a public root program, so a vendor’s own attestation chain is in scope, with the parts we do and do not claim set out on the compliance page. And there is no enrolment ecosystem: no ACME, no SCEP, no EST, no renewal agent. Your pipeline calls the API.
  • Regional replication is opt-in, and priced. Every key is created able to live in more than one region, but by default it lives in one. Standing a key up in a second region is an add-on you pay for, because a standby region is real infrastructure we pay for. The failover procedure is documented with real recovery times rather than marketing zeros.
  • Some infrastructure changes are applied by hand. Not every change reaches production through the pipeline; parts of the platform are applied by the operator at a keyboard, under a written procedure. That is a deliberate trade against a class of automation failure, and it means the review trail for those changes is the pull request and the log, not a pipeline that refused to run.
  • Our strongest sign-in control is configuration, not code. The origin-bound factor requirement described above is set in a console, because the plan we are on will not yet let us define that particular policy as code. Everything else on this page lives in version control, where a change arrives as a diff that has to be reviewed; this one could be weakened from the same console that set it, without a diff and without anything alarming. We know the change that fixes it and what we are waiting on to make it. Until then it is a control we assert, not one you can watch us keep, and it is the only claim here of that kind.
  • One person holds every administrative role. SanctiKey is run by a solo founder, so separation of duties here is procedural, not structural. What stands between a change and production is automated and impersonal: the scanners, the branch protections, the permission ceilings that refuse a change even when the operator meant it. There is no second engineer who could decline to approve, and we will not describe one. The customer-facing path is deliberately built to run without anyone present, which bounds what one person’s absence can reach, and it does not change the fact that one person holds the roles. When that changes, this bullet changes with it.

13. Vulnerability disclosure

Found something? Email security@sanctikey.com. We acknowledge within three business days, we don’t pursue good-faith researchers acting within the policy, and we credit researchers who ask to be credited once a fix ships. The full terms, including the safe harbour grant and the disclosure timeline, are on the vulnerability disclosure policy.