diff --git a/docs.json b/docs.json
index 7f57e7cf..bbaaeec6 100644
--- a/docs.json
+++ b/docs.json
@@ -227,7 +227,8 @@
"solutions/key-management/encryption-key-storage",
"solutions/key-management/enterprise-disaster-recovery",
"solutions/key-management/programmable-credential-access",
- "solutions/key-management/high-security-api-key-storage"
+ "solutions/key-management/api-key-storage",
+ "solutions/key-management/mpc-keyshare-storage"
]
}
]
diff --git a/features/secrets.mdx b/features/secrets.mdx
index d24d949c..3168fe96 100644
--- a/features/secrets.mdx
+++ b/features/secrets.mdx
@@ -1,15 +1,14 @@
---
-title: "Secret Storage"
+title: "Secret storage"
description: "Import, store, and export arbitrary secrets (passwords, credit cards, API keys) with policy-gated, end-to-end encrypted access."
tag: "Beta"
---
import { FeatureCard } from '/snippets/feature-card.mdx'
import { SolutionCard } from '/snippets/solution-card.mdx'
+import { SecretsBetaCallout } from '/snippets/secrets-beta-callout.mdx'
-
- **Closed beta**: the Secrets API is currently in closed beta. [Contact us](https://www.turnkey.com/contact-us) to get onboarded.
-
+
Turnkey Secrets lets you store arbitrary sensitive data (passwords, credit card details, API keys, SSNs) encrypted end-to-end between your client and Turnkey's [secure enclaves](/security/secure-enclaves). The [policy engine](/features/policies/overview) evaluates every export, so you control exactly who can retrieve a secret, under what conditions, and with how many approvals. We designed the secret storage API for flexibility and programmability.
@@ -17,11 +16,13 @@ Plaintext only ever exists inside the enclave and on the client that imported or
## How it works
-**Import**: Turnkey mints a single-use ingress target key inside the enclave, signed by the enclave's quorum key. Your client verifies that signature, encrypts the secret to the target key using HPKE, and submits the ciphertext. The enclave decrypts it, re-encrypts it for storage at rest under a quorum-key-derived key, and deletes the ingress key.
+The Secrets methods use the same enclave secure-channel pattern as [wallet import](/features/wallets/import-wallets) and [wallet export](/features/wallets/export-wallets). Every transfer is HPKE-encrypted to a single-use target key, so plaintext appears only inside the enclave and on the client holding the matching private key. See [Enclave secure channels](/security/enclave-secure-channels) for the canonical protocol details.
-**Export**: the export request carries an ephemeral P-256 target public key, and after policy evaluation approves the request, the enclave decrypts the stored secret and re-encrypts it to that key. The target key is fully configurable: it can belong to the requester, to another agent or service, or to a party that isn't an approver at all. Only the holder of the private half can decrypt the result. The export payload is useless to anyone else, including the approvers themselves.
+What is specific to Secrets:
-For a batch export, the request succeeds only if every policy evaluation returns `ALLOW`. A `DENY`, or any evaluation without an `ALLOW` outcome, rejects the entire batch. Turnkey never exports part of a batch.
+- **Import**: your client encrypts the secret to a single-use ingress target key minted inside the enclave and submits only ciphertext. The enclave re-encrypts it for storage at rest and deletes the ingress key.
+- **Export**: the export request carries an ephemeral P-256 target public key. After policy evaluation approves the request, the enclave re-encrypts the secret to that key. The recipient key is fully configurable: it can belong to the requester, to another agent or service, or to a party that isn't an approver at all. Only the holder of the matching private key can decrypt the result; the payload is useless to anyone else, including the approvers themselves.
+- **Batch export is all-or-nothing**: the request succeeds only if every policy evaluation returns `ALLOW`. A `DENY`, or any evaluation without an `ALLOW` outcome, rejects the entire batch. Turnkey fails closed and never exports part of a batch.
## Static properties
@@ -32,7 +33,7 @@ Secrets are created with optional **static properties**: string key-value pairs
"policyName": "Only the payments agent can export credit cards",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(u, u.tags.contains('payment-agent'))",
- "condition": "activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS' && secret.static_properties['kind'] == 'creditCard'"
+ "condition": "secret.static_properties['kind'] == 'creditCard' && activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS'"
}
```
@@ -65,7 +66,7 @@ const plaintext = await turnkey.apiClient().exportSecret({
});
```
-If the export requires additional approvals, `exportSecret` throws a consensus-needed error. For multi-party flows, including multiple agent instances that co-sign the same export with session keys, use the proposal SDK helpers described in [Programmable Credential Access](/solutions/key-management/programmable-credential-access).
+If the export requires additional approvals, `exportSecret` throws a consensus-needed error. For multi-party flows, including multiple agent instances that co-sign the same export with session keys, use the proposal SDK helpers described in [Programmable credential access](/solutions/key-management/programmable-credential-access).
## Listing secrets
@@ -84,7 +85,7 @@ Because export is an activity, it composes with everything the policy engine sup
Turnkey is a signing and encryption platform running inside secure enclaves, originally built to secure billions of dollars in digital assets. Secret storage is built from the same primitives:
- **End-to-end encryption**: plaintext exists only in enclave memory and on your client. Transport in both directions uses HPKE to single-use P-256 target keys.
-- **Authenticated storage**: at-rest ciphertext is AES-256-GCM under a per-secret key derived from the enclave quorum key, with the organization, secret ID, and cipher suite bound into the authenticated data, so no one can substitute ciphertext across secrets or organizations.
+- **Authenticated storage**: at-rest ciphertext is AES-256-GCM under a per-secret key derived from the enclave quorum key. The organization, secret ID, and cipher suite are bound into the authenticated data, so no one can substitute ciphertext across secrets or organizations.
- **Signed provenance**: the enclave quorum key signs every stored secret and ingress key. Enclaves refuse anything they didn't produce.
- **Quantum resistant internally, agile in transit**: secrets rest under AES-256-GCM, a quantum resistant cipher. The transport cipher suite is a field in import and export requests, designed to be extended over time, so Turnkey can adopt new transport protocols as they mature.
- **Forward secrecy**: ingress and egress target keys are single-use. Compromising one exposes at most one payload.
@@ -93,7 +94,8 @@ Turnkey is a signing and encryption platform running inside secure enclaves, ori
## Next steps
-
-
+
+
+
diff --git a/images/solutions/dark/high-security-api-key-storage.svg b/images/solutions/dark/api-key-storage.svg
similarity index 100%
rename from images/solutions/dark/high-security-api-key-storage.svg
rename to images/solutions/dark/api-key-storage.svg
diff --git a/images/solutions/dark/mpc-keyshare-storage.svg b/images/solutions/dark/mpc-keyshare-storage.svg
new file mode 100644
index 00000000..e313e2ca
--- /dev/null
+++ b/images/solutions/dark/mpc-keyshare-storage.svg
@@ -0,0 +1,9 @@
+
diff --git a/images/solutions/light/high-security-api-key-storage.svg b/images/solutions/light/api-key-storage.svg
similarity index 100%
rename from images/solutions/light/high-security-api-key-storage.svg
rename to images/solutions/light/api-key-storage.svg
diff --git a/images/solutions/light/mpc-keyshare-storage.svg b/images/solutions/light/mpc-keyshare-storage.svg
new file mode 100644
index 00000000..2a491da0
--- /dev/null
+++ b/images/solutions/light/mpc-keyshare-storage.svg
@@ -0,0 +1,9 @@
+
diff --git a/snippets/secrets-beta-callout.mdx b/snippets/secrets-beta-callout.mdx
new file mode 100644
index 00000000..cac8c31f
--- /dev/null
+++ b/snippets/secrets-beta-callout.mdx
@@ -0,0 +1,7 @@
+export const SecretsBetaCallout = () => (
+
+ The Secrets API is currently in closed beta.{" "}
+ Contact us to get
+ onboarded.
+
+);
diff --git a/solutions/key-management/high-security-api-key-storage.mdx b/solutions/key-management/api-key-storage.mdx
similarity index 61%
rename from solutions/key-management/high-security-api-key-storage.mdx
rename to solutions/key-management/api-key-storage.mdx
index 5003e980..3b68e9b1 100644
--- a/solutions/key-management/high-security-api-key-storage.mdx
+++ b/solutions/key-management/api-key-storage.mdx
@@ -1,18 +1,18 @@
---
-title: "High Security API Key Storage"
+title: "API key storage"
description: "Programmatically store and gate access to your most sensitive API keys."
tag: "Beta"
---
import { FeatureCard } from '/snippets/feature-card.mdx'
+import { SolutionCard } from '/snippets/solution-card.mdx'
+import { SecretsBetaCallout } from '/snippets/secrets-beta-callout.mdx'
-
- **Closed beta**: the Secrets API is currently in closed beta. [Contact us](https://www.turnkey.com/contact-us) to get onboarded.
-
+
An exchange API key that can place orders, cancel orders, or move funds is a direct line to your balance sheet. It usually sits in an environment variable, a config file, or a vault that decrypts inside the infrastructure you are trying to protect. An attacker who compromises that infrastructure gets the key too.
-Turnkey removes the plaintext from your infrastructure entirely. The credential lives encrypted inside a [secure enclave](/security/secure-enclaves), and every retrieval is evaluated by the [policy engine](/features/policies/overview) before the enclave releases anything. You decide which service identities can retrieve which keys, under what conditions, and with how many approvals. This solution builds on [Secret Storage](/features/secrets).
+Turnkey keeps plaintext credentials out of your persistent storage and infrastructure intermediaries. The credential lives encrypted inside a [secure enclave](/security/secure-enclaves), and every retrieval is evaluated by the [policy engine](/features/policies/overview) before the enclave releases anything to an authorized recipient. You decide which service identities can retrieve which keys, under what conditions, and with how many approvals. This solution builds on [Secret storage](/features/secrets).
Typical credentials this pattern protects:
@@ -25,7 +25,7 @@ Typical credentials this pattern protects:
| | Vault in your infrastructure | Turnkey |
| :--- | :--- | :--- |
-| Where plaintext exists | Decrypted inside your infrastructure, where a vault admin or a compromised host can read it | Only inside the enclave and on the single authorized recipient |
+| Where plaintext exists | Decrypted inside your infrastructure, where a vault admin or a compromised host can read it | Inside the enclave and transiently in the authorized recipient's memory after export |
| Access control | Enforced by software you operate | Enforced by the policy engine inside the enclave, independent of your infrastructure |
| Multi-party approval | Bolted on, if available | Native consensus: require m-of-n approvals before a key is released |
| Who can read a released secret | Anyone who sees the response | Only the holder of the ephemeral key the payload is encrypted to, which approvers cannot read |
@@ -35,19 +35,19 @@ Typical credentials this pattern protects:
| Decision | What to consider | Learn more |
| :--- | :--- | :--- |
-| **Key classification** | Bind static properties at import time (`exchange`, `permissions`, `environment`, ...) so policies target classes of keys, such as all withdrawal-capable keys, instead of individual IDs. | [Secret Storage](/features/secrets) |
+| **Key classification** | Bind static properties at import time (`exchange`, `permissions`, `environment`, ...) so policies target classes of keys, such as all withdrawal-capable keys, instead of individual IDs. | [Secret storage](/features/secrets) |
| **Service identity** | Model each trading service or environment as a Turnkey user with its own API key or session keys, so retrieval permission is scoped per service. | [Sessions](/features/authentication/sessions) |
| **Approval requirements** | Trade-only keys can allow unilateral retrieval by the trading service. Withdrawal-capable or production keys can require human or multi-party approval. | [Policy Engine](/features/policies/overview) |
-| **Recipient control** | The export payload is encrypted to a single ephemeral public key, so only the service that generated it can read the credential, even when other parties approve. | |
+| **Recipient control** | The export payload is encrypted to a single ephemeral public key, so only the service that generated it can read the credential, even when other parties approve. | [Enclave secure channels](/security/enclave-secure-channels) |
| **Rotation and revocation** | Rotate by importing the new key and deleting the old one. Revoke a service's access instantly by updating policy or removing its credentials. | |
## Example: exchange trading key for a trading firm
-A trading firm holds a long-lived JWT for an execution management system that authorizes placing and canceling orders on crypto exchanges. The plaintext should never exist in the firm's own infrastructure.
+A trading firm holds a long-lived JWT for an execution management system that authorizes placing and canceling orders on crypto exchanges. The plaintext should not be stored at rest in the firm's infrastructure or exposed to intermediaries; after export, it exists transiently in the authorized trading service's memory while in use.
| Need | How Turnkey solves it |
| :--- | :--- |
-| Plaintext never lives in the firm's infrastructure | The firm imports the JWT once over an end-to-end encrypted channel, and it stays inside the enclave. Retrieval re-encrypts it to a single ephemeral key |
+| No plaintext at rest or in intermediaries | The firm imports the JWT once over an end-to-end encrypted channel. Retrieval re-encrypts it to the authorized trading service's ephemeral key |
| Only the trading service can retrieve it | Policy scopes retrieval of `kind == 'exchangeApiKey'` secrets to the trading service user |
| High-risk keys need oversight | A consensus policy requires human approval before any withdrawal-capable key is released |
| Every access is attributable | Each retrieval and approval is a signed activity, logged and queryable |
@@ -59,7 +59,7 @@ A trading firm holds a long-lived JWT for an execution management system that au
"policyName": "Trading service can retrieve trade-only exchange keys",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(u, u.id == '')",
- "condition": "activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS' && secret.static_properties['kind'] == 'exchangeApiKey' && secret.static_properties['permissions'] == 'trade'"
+ "condition": "secret.static_properties['kind'] == 'exchangeApiKey' && secret.static_properties['permissions'] == 'trade' && activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS'"
}
```
@@ -81,11 +81,11 @@ A trading firm holds a long-lived JWT for an execution management system that au
});
```
- You can now delete the plaintext from wherever it existed before.
+ You can now delete any persisted plaintext copy from wherever it existed before.
- The trading service authenticates with its own credentials and retrieves the key when it boots or opens a session. `exportSecret` generates the ephemeral keypair, submits the export activity, decrypts the result, and zeroizes the key:
+ The trading service authenticates with its own credentials and retrieves the key when it boots or opens a session. `exportSecret` generates the ephemeral keypair, submits the export activity, and decrypts the result into the authorized service's memory. Symmetric, bearer, HMAC, and OAuth credentials necessarily exist there transiently while in use:
```typescript
const emsJwt = await turnkey.apiClient().exportSecret({
@@ -104,7 +104,7 @@ A trading firm holds a long-lived JWT for an execution management system that au
"policyName": "Withdrawal-capable keys require a human approver",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(u, u.id == '') && approvers.any(u, u.tags.contains('risk-admin'))",
- "condition": "activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS' && secret.static_properties['kind'] == 'exchangeApiKey' && secret.static_properties['permissions'] == 'withdraw'"
+ "condition": "secret.static_properties['kind'] == 'exchangeApiKey' && secret.static_properties['permissions'] == 'withdraw' && activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS'"
}
```
@@ -116,9 +116,17 @@ A trading firm holds a long-lived JWT for an execution management system that au
+### Variant: trading on behalf of your users
+
+Some firms run trading strategies on behalf of other people rather than on their own exchange accounts. The same pattern supports a non-custodial setup: each end user gets their own [sub-organization](/features/sub-organizations) and imports their exchange API key into it directly, so the plaintext never passes through your infrastructure on the way in. A [delegated access](/features/policies/delegated-access/overview) user you control, scoped by policy to retrieving that key and nothing else, lets your trading strategy pull the credential at runtime while the end user retains control of their sub-organization.
+
+For a trust-minimized deployment, run the trading strategy itself inside [Turnkey Verifiable Cloud](/features/verifiable-cloud/overview). The strategy executes in a verifiable secure enclave, so end users can verify exactly what code receives their API key, and the plaintext exists only inside that enclave while the strategy runs, keeping the arrangement non-custodial end to end.
+
## Next steps
-
+
+
+
diff --git a/solutions/key-management/mpc-keyshare-storage.mdx b/solutions/key-management/mpc-keyshare-storage.mdx
new file mode 100644
index 00000000..3b7c955c
--- /dev/null
+++ b/solutions/key-management/mpc-keyshare-storage.mdx
@@ -0,0 +1,65 @@
+---
+title: "MPC keyshare storage"
+description: "Enclave-protected, policy-gated backup and recovery for MPC keyshare bundles, stored as opaque secrets."
+tag: "Beta"
+---
+
+import { FeatureCard } from '/snippets/feature-card.mdx'
+import { SolutionCard } from '/snippets/solution-card.mdx'
+import { SecretsBetaCallout } from '/snippets/secrets-beta-callout.mdx'
+
+
+
+Regulated custodians and institutions that sign with MPC systems distribute keyshares across parties, and typically rely on an independent backup or recovery provider so that losing one party's share does not mean losing funds. Turnkey Secrets can serve as that independent recovery store. An MPC party's keyshare bundle is imported as an opaque secret, held inside a [secure enclave](/security/secure-enclaves), and released only through a [policy-gated](/features/policies/overview) export encrypted to a designated recovery participant. You can use it instead of, or in addition to, another recovery store. This solution builds on [Secret storage](/features/secrets).
+
+## Opaque by design
+
+Turnkey treats every keyshare as an opaque byte blob. Turnkey never parses, validates, interprets, or derives anything from the bundle's contents, and makes no assumptions about curve, seed format, or addresses. Whatever bytes your MPC provider exports are the bytes Turnkey stores and returns.
+
+## What Turnkey contributes
+
+| Need | How Turnkey solves it |
+| :--- | :--- |
+| Keyshare bundle never sits plaintext in your infrastructure | Enclave-protected opaque storage: the bundle is end-to-end encrypted into the enclave at import and only ever leaves re-encrypted to a designated recipient |
+| Recovery must not be unilateral | Policy-gated export with independently controllable approvals: require consensus from risk officers or recovery operators that you manage separately from the MPC system |
+| Only the recovery participant may read the released share | The export payload is encrypted to a single ephemeral public key generated by the designated recovery participant; approvers cannot read it |
+| Recovery events must be auditable | Every import, export, and approval is a signed, attributable activity, logged and queryable |
+
+## Classifying and gating keyshares
+
+Bind [static properties](/features/secrets#static-properties) at import time so policies target classes of keyshares instead of individual IDs:
+
+```typescript
+const secretId = await turnkey.apiClient().importSecret({
+ plaintext: keyshareBundle,
+ name: "treasury-signer-party-2-keyshare",
+ staticProperties: {
+ kind: "mpcKeyshare",
+ curve: "secp256k1",
+ environment: "production",
+ recoveryRole: "backupParty",
+ },
+});
+```
+
+Gate export on those properties. For example, require two recovery operators to approve before any production keyshare is released:
+
+```json
+{
+ "policyName": "Production MPC keyshares require two recovery operators",
+ "effect": "EFFECT_ALLOW",
+ "consensus": "approvers.filter(u, u.tags.contains('recovery-operator')).count() >= 2",
+ "condition": "secret.static_properties['kind'] == 'mpcKeyshare' && secret.static_properties['environment'] == 'production' && activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS'"
+}
+```
+
+At recovery time, the designated recovery participant generates an ephemeral keypair and retrieves the bundle with `exportSecret`, or with the proposal helpers described in [Programmable credential access](/solutions/key-management/programmable-credential-access) when the export requires additional approvals. Only that participant can decrypt the released bundle, which then re-enters your MPC provider's own recovery procedure.
+
+## Next steps
+
+
+
+
+
+
+
diff --git a/solutions/key-management/overview.mdx b/solutions/key-management/overview.mdx
index dac42781..91876bec 100644
--- a/solutions/key-management/overview.mdx
+++ b/solutions/key-management/overview.mdx
@@ -69,17 +69,23 @@ Key management serves different needs depending on how your application uses cry
description="Import and recover wallets with end-to-end encryption, quorum-controlled access, and a cryptographic audit trail for treasury recovery, provider migration, and failover."
/>
+
## Ready to build?
diff --git a/solutions/key-management/programmable-credential-access.mdx b/solutions/key-management/programmable-credential-access.mdx
index dba8067e..35a5d567 100644
--- a/solutions/key-management/programmable-credential-access.mdx
+++ b/solutions/key-management/programmable-credential-access.mdx
@@ -1,18 +1,18 @@
---
-title: "Programmable Credential Access"
+title: "Programmable credential access"
description: "A password manager built for machines: policy-gated, programmable access to secrets for humans, services, and AI agents."
tag: "Beta"
---
import { FeatureCard } from '/snippets/feature-card.mdx'
+import { SolutionCard } from '/snippets/solution-card.mdx'
+import { SecretsBetaCallout } from '/snippets/secrets-beta-callout.mdx'
-
- **Closed beta**: the Secrets API is currently in closed beta. [Contact us](https://www.turnkey.com/contact-us) to get onboarded.
-
+
When an AI agent needs a password, a card number, or an API key, teams face an unpleasant choice: hand the agent unfettered access to credentials, or put a human in the loop for every single request. Turnkey gives you a third option: a programmable access layer that evaluates every credential request against policies you control, inside a [secure enclave](/security/secure-enclaves) that never releases plaintext unless the policy allows it.
-Policies and tags are fully dynamic: update them at runtime to widen or narrow access on the fly, without re-importing anything. This solution builds on [Secret Storage](/features/secrets).
+Policies and tags are fully dynamic: update them at runtime to widen or narrow access on the fly, without re-importing anything. This solution builds on [Secret storage](/features/secrets).
## Access patterns
@@ -28,10 +28,10 @@ One policy engine supports the full spectrum of trust models:
| Decision | What to consider | Learn more |
| :--- | :--- | :--- |
-| **Secret classification** | Bind static properties at import time (`kind`, `environment`, `requiresConsensus`, ...) so policies target classes of secrets instead of individual IDs. | [Secret Storage](/features/secrets) |
+| **Secret classification** | Bind static properties at import time (`kind`, `environment`, `requiresConsensus`, ...) so policies target classes of secrets instead of individual IDs. | [Secret storage](/features/secrets) |
| **Agent identity** | Model each agent type or role as a durable Turnkey user. A scoped, expiring session key authenticates each ephemeral instance of that user, making credential delegation easy to express in policy. | [Sessions](/features/authentication/sessions) |
| **Consensus requirements** | Decide which secret classes need one agent role, several, or a specific combination of user tags (e.g. one `browser-agent` *and* one `payment-agent`). | [Policy Engine](/features/policies/overview) |
-| **Recipient control** | The export payload is encrypted to a single ephemeral public key. Decide which party generates that key. That party, and only that party, can read the secret. | |
+| **Recipient control** | The export payload is encrypted to a single ephemeral public key. Decide which party generates that key. That party, and only that party, can read the secret. | [Enclave secure channels](/security/enclave-secure-channels) |
| **Revocation** | Invalidate a session key to revoke one agent instance, or delete the agent-role user to revoke every instance of that role. | [Sessions](/features/authentication/sessions) |
## Example: multi-agent consensus for payments
@@ -52,7 +52,7 @@ Model a browser agent and a payment agent as durable Turnkey users, with session
"policyName": "Require two agent roles for credit card access",
"effect": "EFFECT_ALLOW",
"consensus": "approvers.any(u, u.tags.contains('browser-agent')) && approvers.any(u, u.tags.contains('payment-agent'))",
- "condition": "activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS' && secret.static_properties['requiresConsensus'] == 'true' && secret.static_properties['kind'] == 'creditCard'"
+ "condition": "secret.static_properties['requiresConsensus'] == 'true' && secret.static_properties['kind'] == 'creditCard' && activity.type == 'ACTIVITY_TYPE_EXPORT_SECRETS'"
}
```
@@ -126,6 +126,8 @@ Model a browser agent and a payment agent as durable Turnkey users, with session
## Next steps
-
+
+
+
diff --git a/solutions/overview.mdx b/solutions/overview.mdx
index d3664ff1..99a4ba34 100644
--- a/solutions/overview.mdx
+++ b/solutions/overview.mdx
@@ -83,15 +83,21 @@ Enterprise-grade security for your most sensitive keys — hardware-backed with
description="Non-custodial wallet recovery with instant policy enforcement."
/>
+
diff --git a/welcome.mdx b/welcome.mdx
index d8c49e03..30814138 100644
--- a/welcome.mdx
+++ b/welcome.mdx
@@ -270,7 +270,7 @@ mode: "custom"
- Programmable Credential Access
+ Programmable credential access