diff --git a/docs-mintlify/api/overview.mdx b/docs-mintlify/api/overview.mdx
index 4562fd205..a893e49b9 100644
--- a/docs-mintlify/api/overview.mdx
+++ b/docs-mintlify/api/overview.mdx
@@ -86,14 +86,14 @@ curl https://api.hexclave.com/api/v1/ \
Never use server access type or secret server keys in client-side code, browser requests, or any publicly accessible location. Always keep server keys secure on your backend.
- For more information, see the concept documentation on [StackApp](/guides/going-further/hexclave-app#client-vs-server).
+ For more information, see the [`HexclaveClientApp` and `HexclaveServerApp` SDK reference](/sdk/objects/hexclave-app).
If you'd like to build your own version of the Stack dashboard (or update project configuration programmatically), you can use the `admin` access type. These endpoints are very dangerous and you should only use them if you know what you're doing.
- For more information, see the concept documentation on [StackApp](/guides/going-further/hexclave-app#client-vs-server).
+ For more information, see the [`HexclaveClientApp` and `HexclaveServerApp` SDK reference](/sdk/objects/hexclave-app).
diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json
index 54e9461be..80969c0d1 100644
--- a/docs-mintlify/docs.json
+++ b/docs-mintlify/docs.json
@@ -75,12 +75,10 @@
{
"group": "Going Further",
"pages": [
- "guides/going-further/hexclave-app",
"guides/going-further/backend-integration",
"guides/going-further/cli",
"guides/going-further/local-vs-cloud-dashboard",
- "guides/going-further/hexclave-config",
- "guides/going-further/user-metadata"
+ "guides/going-further/hexclave-config"
]
},
{
@@ -249,10 +247,6 @@
"customScripts": ["/apps-sidebar-filter.js", "/code-language-labels.js"]
},
"redirects": [
- {
- "source": "/guides/going-further/stack-app",
- "destination": "/guides/going-further/hexclave-app"
- },
{
"source": "/sdk/objects/stack-app",
"destination": "/sdk/objects/hexclave-app"
@@ -299,11 +293,19 @@
},
{
"source": "/docs/concepts/custom-user-data",
- "destination": "/guides/going-further/user-metadata"
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
+ },
+ {
+ "source": "/guides/going-further/user-metadata",
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
},
{
"source": "/others/js-client",
- "destination": "/guides/going-further/hexclave-app"
+ "destination": "/sdk/objects/hexclave-app"
+ },
+ {
+ "source": "/guides/going-further/stack-app",
+ "destination": "/sdk/objects/hexclave-app"
}
]
}
diff --git a/docs-mintlify/guides/apps/authentication/user-onboarding.mdx b/docs-mintlify/guides/apps/authentication/user-onboarding.mdx
index 44d6017c9..843d4aa40 100644
--- a/docs-mintlify/guides/apps/authentication/user-onboarding.mdx
+++ b/docs-mintlify/guides/apps/authentication/user-onboarding.mdx
@@ -15,7 +15,7 @@ Instead, a more reliable strategy is to store an `onboarded` flag in the user's
## Example implementation
-Let's say you have an onboarding page that asks for an address and stores it in the user's [metadata](/guides/going-further/user-metadata):
+Let's say you have an onboarding page that asks for an address and stores it in the user's [metadata](/guides/getting-started/user-fundamentals#custom-metadata):
```jsx
export default function OnboardingPage() {
diff --git a/docs-mintlify/guides/getting-started/user-fundamentals.mdx b/docs-mintlify/guides/getting-started/user-fundamentals.mdx
index 93a47ea08..fc1ac88cd 100644
--- a/docs-mintlify/guides/getting-started/user-fundamentals.mdx
+++ b/docs-mintlify/guides/getting-started/user-fundamentals.mdx
@@ -110,13 +110,15 @@ export default function MyClientComponent() {
### Custom metadata
-Beyond built-in fields like `displayName` and `primaryEmail`, you'll often need to store your own data on a user. Hexclave provides three metadata fields for this:
+Beyond built-in fields like `displayName` and `primaryEmail`, you can store your own JSON-like data on a user. Hexclave provides three metadata fields, each with different read and write permissions:
-- **`clientMetadata`** — Readable and writable from both the client and the server. Use this for non-sensitive user preferences like theme, locale, or UI state.
-- **`serverMetadata`** — Readable and writable only from the server. Use this for sensitive or internal data like internal flags, or anything users shouldn't be able to see or modify.
-- **`clientReadOnlyMetadata`** — Readable from the client, writable only from the server. Use this for data the client needs to display but shouldn't be able to change, like subscription plans or role labels.
+| Field | Client access | Server access | Use for |
+| --- | --- | --- | --- |
+| `clientMetadata` | Read and write | Read and write | Non-sensitive user preferences, such as theme, locale, onboarding form drafts, or UI state. |
+| `serverMetadata` | No access | Read and write | Sensitive or internal data that users should not be able to see or modify. |
+| `clientReadOnlyMetadata` | Read only | Read and write | Server-authoritative data that the client should display, such as subscription plans, role labels, or validated onboarding state. |
-For example, storing a user's theme preference in `clientMetadata`:
+Use `clientMetadata` for information that is safe for the browser to read and change. For example, a user can store their own theme preference:
```ts my-app.ts
@@ -154,10 +156,10 @@ export default function ThemeToggle() {
```
-And storing sensitive data in `serverMetadata` (server-side only):
+Use `serverMetadata` for sensitive or internal data. It is only available through [`HexclaveServerApp`](/sdk/objects/hexclave-app#hexclaveserverapp):
-```ts my-app.ts
+```ts my-server-file.ts
import { hexclaveServerApp } from "../src/stack/server";
const user = await hexclaveServerApp.getUser();
@@ -166,10 +168,42 @@ await user.update({
internalFlag: true,
},
});
+
+console.log(user.serverMetadata);
```
-For more details, see the [User Metadata](/guides/going-further/user-metadata) documentation.
+Use `clientReadOnlyMetadata` when the client needs to read a value but only your server should decide it. A common example is storing a validated onboarding or subscription state:
+
+
+```ts my-server-file.ts
+import { hexclaveServerApp } from "../src/stack/server";
+
+const user = await hexclaveServerApp.getUser({ or: "throw" });
+await user.update({
+ clientReadOnlyMetadata: {
+ subscriptionPlan: "premium",
+ onboarded: true,
+ },
+});
+```
+
+```tsx my-client-component.tsx
+'use client';
+import { useUser } from "@hexclave/next";
+
+export default function SubscriptionBadge() {
+ const user = useUser({ or: "redirect" });
+ const plan = user.clientReadOnlyMetadata?.subscriptionPlan ?? "free";
+
+ return Current plan: {plan}
;
+}
+```
+
+
+
+ Do not put secrets or trusted authorization decisions in `clientMetadata`. Users can read and modify it from client-side code. If the value affects access control, validate it on your server and store it in `serverMetadata` or `clientReadOnlyMetadata`.
+
## Signing out
diff --git a/docs-mintlify/guides/going-further/hexclave-app.mdx b/docs-mintlify/guides/going-further/hexclave-app.mdx
deleted file mode 100644
index 3c06102c5..000000000
--- a/docs-mintlify/guides/going-further/hexclave-app.mdx
+++ /dev/null
@@ -1,50 +0,0 @@
----
-title: "Stack App"
-description: "The most important object of your Stack project"
-sidebarTitle: "The StackApp Object"
----
-
-By now, you may have seen the `useHexclaveApp()` hook and the `hexclaveServerApp` variable. Both return a `StackApp`, of type `HexclaveClientApp` and `HexclaveServerApp` respectively.
-
-Nearly all of Stack's functionality is on your `StackApp` object. Think of this object as the "connection" from your code to Stack's servers. Each app is always associated with one specific project ID (by default the one found in your environment variables).
-
-There is also a page on [StackApp](/sdk/objects/hexclave-app) in the SDK reference, which lists all available functions.
-
-## `getXyz`/`listXyz` vs. `useXyz`
-
-You will see that most of the asynchronous functions on `StackApp` come in two flavors: `getXyz`/`listXyz` and `useXyz`. The former are asynchronous fetching functions which return a `Promise`, while the latter are React hooks that [suspend](https://react.dev/reference/react/Suspense) the current component until the data is available.
-
-Normally, you would choose between the two based on whether you are in a React Server Component or a React Client Component. However, there are some scenarios where you use `getXyz` on the client, for example as the callback of an `onClick` handler.
-
-```tsx
-// server-component.tsx
-async function ServerComponent() {
- const app = hexclaveServerApp;
- // returns a Promise, must be awaited
- const user = await app.getUser();
-
- return
{user.displayName}
;
-}
-
-// client-component.tsx
-"use client";
-function ClientComponent() {
- const app = useHexclaveApp();
- // returns the value directly
- const user = app.useUser();
-
- return {user.displayName}
;
-}
-```
-
-## Client vs. server
-
-`HexclaveClientApp` contains everything needed to build a frontend application, for example the currently authenticated user. It requires a publishable client key in its initialization (usually set by the `NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY` environment variable).
-
-`HexclaveServerApp` has all the functionality of `HexclaveClientApp`, but also some functions with elevated permissions, eg. listing or modifying ALL users. This requires a secret server key (usually set by the `STACK_SECRET_SERVER_KEY` environment variable), which **must always be kept secret**.
-
-There is also a third type, `HexclaveAdminApp`, but it is rarely used. You can use it for automation or internal tools, and can edit your project's configuration.
-
-
- Some of the functions have different return types; for example, `HexclaveClientApp.getUser()` returns a `Promise` while `HexclaveServerApp.getUser()` returns a `Promise`. The `Server` or `Admin` prefixes indicate that the object contains server-/admin-only functionality.
-
diff --git a/docs-mintlify/guides/going-further/hexclave-config.mdx b/docs-mintlify/guides/going-further/hexclave-config.mdx
index 57b41a92d..d33a8dae5 100644
--- a/docs-mintlify/guides/going-further/hexclave-config.mdx
+++ b/docs-mintlify/guides/going-further/hexclave-config.mdx
@@ -36,10 +36,6 @@ If you are running Hexclave on a [cloud project](/guides/going-further/local-vs-
Most nested maps use IDs that you choose, such as `payments.products.pro` or `rbac.permissions.admin`. Unless a field says otherwise, custom IDs can contain letters, numbers, underscores, and hyphens, must not start with a hyphen, and can be up to 63 characters long.
-
- `hexclave.config.ts` must be statically readable. Export a plain object or `"show-onboarding"`; do not use functions, spreads, computed keys, imports as values, or environment-dependent expressions inside the exported config.
-
-
## Top-Level Sections
| Section | What it controls |
@@ -213,7 +209,7 @@ For more information on creating, listing, and validating API keys, see the [API
| `users` | `{ allowClientUserDeletion?: boolean }` | `{ allowClientUserDeletion: false }` | Configures user self-service behavior. `allowClientUserDeletion` allows users to delete their own account from the client. |
| `onboarding` | `{ requireEmailVerification?: boolean }` | `{ requireEmailVerification: false }` | Configures onboarding requirements. `requireEmailVerification` requires users to verify their email as part of onboarding. |
-For more information on team behavior, see [Teams](/guides/apps/teams/overview) and [Team Selection](/guides/apps/teams/team-selection). For user profile and onboarding behavior, see [User Fundamentals](/guides/getting-started/user-fundamentals), [User Onboarding](/guides/apps/authentication/user-onboarding), and [User Metadata](/guides/going-further/user-metadata).
+For more information on team behavior, see [Teams](/guides/apps/teams/overview) and [Team Selection](/guides/apps/teams/team-selection). For user profile, metadata, and onboarding behavior, see [User Fundamentals](/guides/getting-started/user-fundamentals#custom-metadata) and [User Onboarding](/guides/apps/authentication/user-onboarding).
## Emails
diff --git a/docs-mintlify/guides/going-further/user-metadata.mdx b/docs-mintlify/guides/going-further/user-metadata.mdx
deleted file mode 100644
index b4d079845..000000000
--- a/docs-mintlify/guides/going-further/user-metadata.mdx
+++ /dev/null
@@ -1,61 +0,0 @@
----
-title: "User Metadata"
-description: "How to store custom user metadata in Hexclave"
----
-
-Hexclave allows storing additional user information through three types of metadata fields:
-
-1. **clientMetadata**: Readable and writable from a [client](/guides/going-further/hexclave-app#client-vs-server).
-2. **serverMetadata**: Readable and writable only from a [server](/guides/going-further/hexclave-app#client-vs-server).
-3. **clientReadOnlyMetadata**: Readable from a client, writable only from a server.
-
-## Client metadata
-
-You can use the `clientMetadata` field to store non-sensitive information that both the client and server can read and write.
-
-```tsx
-await user.update({
- clientMetadata: {
- mailingAddress: "123 Main St",
- },
-});
-
-// On the client:
-const user = useUser();
-console.log(user.clientMetadata);
-```
-
-## Server-side metadata
-
-For sensitive information, use the `serverMetadata` field. This ensures the data is only accessible and modifiable by the server.
-
-```tsx
-const user = await hexclaveServerApp.getUser();
-await user.update({
- serverMetadata: {
- secretInfo: "This is a secret",
- },
-});
-
-// To read:
-const user = await hexclaveServerApp.getUser();
-console.log(user.serverMetadata);
-```
-
-## Client read-only metadata
-
-Use `clientReadOnlyMetadata` for data that clients need to read but never modify, such as subscription status.
-
-```tsx
-// On the server:
-const user = await hexclaveServerApp.getUser();
-await user.update({
- clientReadOnlyMetadata: {
- subscriptionPlan: "premium",
- },
-});
-
-// On the client:
-const user = useUser();
-console.log(user.clientReadOnlyMetadata);
-```
diff --git a/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx b/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
index 8bec7249e..86b91b285 100644
--- a/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
+++ b/docs-mintlify/guides/other/tutorials/build-a-saas-with-hexclave.mdx
@@ -302,7 +302,7 @@ Before going live, tighten **callback domains**, replace shared **OAuth** keys w
| Topic | Guide |
|--------|--------|
| Install and configure | [Setup](/guides/getting-started/setup) |
-| `StackApp` object | [Stack App](/guides/going-further/hexclave-app) |
+| `HexclaveApp` object | [HexclaveApp SDK reference](/sdk/objects/hexclave-app) |
| Current user and page protection | [User fundamentals](/guides/getting-started/user-fundamentals) |
| Teams, membership, and RBAC (deeper path) | [Build a team-based app](/guides/other/tutorials/build-a-team-based-app) |
| Teams reference | [Teams](/guides/apps/teams/overview) |
diff --git a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
index dbf7ee50e..361969c90 100644
--- a/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
+++ b/docs-mintlify/guides/other/tutorials/ship-production-ready-auth.mdx
@@ -106,7 +106,7 @@ Read the full discussion in [User fundamentals — Protecting a page](/guides/ge
## 2. Secrets, keys, and environments
- **`STACK_SECRET_SERVER_KEY`** (or `ssk_...`) must **only** exist in server-side environments (SSR, route handlers, server actions, your backend). Never prefix it with `NEXT_PUBLIC_`, never import it from code that runs in the browser, and never log it. See [Stack App](/guides/going-further/hexclave-app) and the [REST API overview](/api/overview).
+ **`STACK_SECRET_SERVER_KEY`** (or `ssk_...`) must **only** exist in server-side environments (SSR, route handlers, server actions, your backend). Never prefix it with `NEXT_PUBLIC_`, never import it from code that runs in the browser, and never log it. See the [HexclaveApp SDK reference](/sdk/objects/hexclave-app) and the [REST API overview](/api/overview).
Practical split:
@@ -156,7 +156,7 @@ If you consume Stack webhooks, **verify every payload** (for example with Svix a
| First integration | [Build a SaaS with Hexclave](/guides/other/tutorials/build-a-saas-with-hexclave) |
| Page protection details | [User fundamentals](/guides/getting-started/user-fundamentals) |
| Domains, OAuth, email, prod mode | [Launch checklist](/guides/apps/launch-checklist/overview) |
-| `HexclaveServerApp` and keys | [Stack App](/guides/going-further/hexclave-app) |
+| `HexclaveServerApp` and keys | [HexclaveApp SDK reference](/sdk/objects/hexclave-app) |
| Webhook verification | [Webhooks](/guides/apps/webhooks/overview) |
| REST from your backend | [API overview](/api/overview) |
diff --git a/docs-mintlify/index.mdx b/docs-mintlify/index.mdx
index 6f29003c4..18f55df0c 100644
--- a/docs-mintlify/index.mdx
+++ b/docs-mintlify/index.mdx
@@ -148,12 +148,11 @@ export const copyGeneratedSetupPrompt = async (event) => {
{/* Going Further */}
-
Going Further
+
Going Further
- Configure Stack App deeply, integrate your backend, and use lower-level interfaces where needed.
+ Compare development flows, integrate your backend, and use lower-level interfaces where needed.
- Stack App
Backend Integration
Local vs. Cloud
Config File
diff --git a/docs-mintlify/llms-full.txt b/docs-mintlify/llms-full.txt
index 68fb9604c..e71eb84f9 100644
--- a/docs-mintlify/llms-full.txt
+++ b/docs-mintlify/llms-full.txt
@@ -35,12 +35,10 @@ The full docs sidebar — generated from the live navigation. Fetch any of these
- [User Fundamentals](https://docs.hexclave.com/guides/getting-started/user-fundamentals)
- [AI Integration](https://docs.hexclave.com/guides/getting-started/ai-integration)
- **Going Further**
- - [Hexclave App](https://docs.hexclave.com/guides/going-further/hexclave-app)
- [Backend Integration](https://docs.hexclave.com/guides/going-further/backend-integration)
- [CLI](https://docs.hexclave.com/guides/going-further/cli)
- [Local Vs Cloud Dashboard](https://docs.hexclave.com/guides/going-further/local-vs-cloud-dashboard)
- [Hexclave Config](https://docs.hexclave.com/guides/going-further/hexclave-config)
- - [User Metadata](https://docs.hexclave.com/guides/going-further/user-metadata)
- **Apps**
- **Authentication**
- [Authentication](https://docs.hexclave.com/guides/apps/authentication/overview)
diff --git a/docs-mintlify/sdk/objects/hexclave-app.mdx b/docs-mintlify/sdk/objects/hexclave-app.mdx
index ad11e2ece..7c921fb69 100644
--- a/docs-mintlify/sdk/objects/hexclave-app.mdx
+++ b/docs-mintlify/sdk/objects/hexclave-app.mdx
@@ -14,7 +14,7 @@ import {
MethodReturns,
} from "/snippets/sdk-type-components.jsx";
-This is a detailed reference for the `StackApp` object. If you're looking for a more high-level overview, please read the [respective page in the Concepts section](/guides/going-further/hexclave-app).
+This is a detailed reference for the `StackApp` object. For setup instructions, see [Setup](/guides/getting-started/setup).
## Overview
@@ -496,7 +496,7 @@ If you're building a client-only app and don't have a `SECRET_SERVER_KEY`, you c
# HexclaveServerApp
-Like `HexclaveClientApp`, but with [server permissions](/guides/going-further/hexclave-app#client-vs-server). Has full read and write access to all users.
+Like `HexclaveClientApp`, but with server permissions. Has full read and write access to all users.
Since this functionality should only be available in environments you trust
diff --git a/docs-mintlify/sdk/types/project.mdx b/docs-mintlify/sdk/types/project.mdx
index 5b831c870..2d31a072d 100644
--- a/docs-mintlify/sdk/types/project.mdx
+++ b/docs-mintlify/sdk/types/project.mdx
@@ -26,7 +26,7 @@ export const ProjectSection = (props) => (
The `Project` object contains the information and configuration of a project, such as the name, description, and enabled authentication methods.
-Each [Stack app](/guides/going-further/hexclave-app) corresponds to a project. You can obtain its `Project` object by calling [`hexclaveApp.getProject()`](/sdk/objects/hexclave-app#stackclientappgetproject) or [`hexclaveApp.useProject()`](/sdk/objects/hexclave-app#stackclientappuseproject).
+Each [`HexclaveApp`](/sdk/objects/hexclave-app) corresponds to a project. You can obtain its `Project` object by calling [`hexclaveApp.getProject()`](/sdk/objects/hexclave-app#stackclientappgetproject) or [`hexclaveApp.useProject()`](/sdk/objects/hexclave-app#stackclientappuseproject).
## Table of Contents
diff --git a/docs-mintlify/sdk/types/team.mdx b/docs-mintlify/sdk/types/team.mdx
index ec88f0714..e276ad282 100644
--- a/docs-mintlify/sdk/types/team.mdx
+++ b/docs-mintlify/sdk/types/team.mdx
@@ -752,7 +752,7 @@ You can get `Team` objects with the `user.useTeams()` or `user.listTeams()` func
# `ServerTeam`
-Like [`Team`](#team), but with [server permissions](/guides/going-further/hexclave-app#client-vs-server). Has full read and write access to everything.
+Like [`Team`](#team), but with server permissions through [`HexclaveServerApp`](/sdk/objects/hexclave-app#hexclaveserverapp). Has full read and write access to everything.
Calling `serverUser.getTeam(...)` and `serverUser.listTeams()` will return `ServerTeam` objects if the user is a [`ServerUser`](/sdk/types/user#serveruser). Alternatively, you can call `hexclaveServerApp.getTeam('team_id_123')` or `hexclaveServerApp.listTeams()` to query all teams of the project.
diff --git a/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts b/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
index 96d4ca88e..87410f170 100644
--- a/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
+++ b/packages/shared/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts
@@ -77,12 +77,10 @@ const docsJson = {
{
"group": "Going Further",
"pages": [
- "guides/going-further/hexclave-app",
"guides/going-further/backend-integration",
"guides/going-further/cli",
"guides/going-further/local-vs-cloud-dashboard",
- "guides/going-further/hexclave-config",
- "guides/going-further/user-metadata"
+ "guides/going-further/hexclave-config"
]
},
{
@@ -259,10 +257,6 @@ const docsJson = {
]
},
"redirects": [
- {
- "source": "/guides/going-further/stack-app",
- "destination": "/guides/going-further/hexclave-app"
- },
{
"source": "/sdk/objects/stack-app",
"destination": "/sdk/objects/hexclave-app"
@@ -309,11 +303,19 @@ const docsJson = {
},
{
"source": "/docs/concepts/custom-user-data",
- "destination": "/guides/going-further/user-metadata"
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
+ },
+ {
+ "source": "/guides/going-further/user-metadata",
+ "destination": "/guides/getting-started/user-fundamentals#custom-metadata"
},
{
"source": "/others/js-client",
- "destination": "/guides/going-further/hexclave-app"
+ "destination": "/sdk/objects/hexclave-app"
+ },
+ {
+ "source": "/guides/going-further/stack-app",
+ "destination": "/sdk/objects/hexclave-app"
}
]
} as const;