Documentation / Build
Account UI and template reuse
All in-app account controls live in src/components/account and compose the same Base UI/shadcn primitives as the rest of the app. Hexclave provides identity and service operations; Convex continues to own application data. No Hexclave account visual components or theme wrapper are mounted in the application.
The real account components are registered in the Account group at /components. Their previews import the same views used by the connected app components and supply only local sample data/actions. Source tabs display their actual implementation. Follow the component source-of-truth workflow when changing or extending UI; bun run check:components enforces catalog coverage and import boundaries.
Entry and authentication
AuthButtons is the reusable entry component. It resolves the current session before showing actions:
- Signed out or anonymous: Log in, plus Sign up when the project permits registration.
- Signed in: Dashboard.
- Restricted account: Continue account setup, opening hosted onboarding.
- Loading: the shared auth-shaped
ContentSkeleton, without flashing signed-out actions.
Login, signup, recovery, verification, MFA challenges, and authentication callbacks remain Hexclave-hosted. The SDK handles cross-domain authentication and returns users to /app. /app/settings is a custom local route, with sections addressable through ?tab=profile, security, notifications, sessions, teams, api-keys, billing, and account.
Authentication follows the official setup and route protection guide: one nextjs-cookie client is passed to HexclaveProvider in the root layout, and client components use useHexclaveApp() / useUser() from that provider. The /app layout uses useUser({ or: "redirect" }); Hexclave owns PKCE, callbacks, session persistence, and onboarding. There is no separate memory-token client, custom callback exchange, or server redirect back to the homepage. Convex is mounted inside the authenticated app, so public sign-in and sign-out remain usable independently of application data. Initialized product homepages use AuthButtons, including SDK sign-out for signed-in and restricted users. The kit marketing homepage links to the separate demo application.
The (workspace) layout serves only the application routes. Components, Brand, Icons, and Docs remain public and statically generated under (public)/(resources), whose shell has no account controls or session lookups. The application shell uses optional session lookups only inside account loading/error boundaries; the /app layout keeps useUser({ or: "redirect" }) and keys DataProvider by the authenticated user ID. The Convex client is created lazily and reused within the browser, with a fresh auth callback on account changes. Private query content still waits for Convex authentication. Team changes preserve the provider and personal-note ownership. The shared shell's page loading/error boundary keeps navigation available.
Available local components
UserMenu: avatar, account details, Overview/Settings links, and sign-out.TeamSwitcher: dropdown listing memberships, Personal account, Manage teams, and project-permitted team creation, including an empty state.AppShell: shared Overview and Settings application frame, with a persistent collapsible desktop sidebar and mobile drawer.AppNavigationViewrenders those application destinations with a separate Website and resources group. Optional account slots load independently of application content; the header holds the account menu and the sidebar holds team selection and appearance.AccountSettings: local settings navigation and isolated loading/error boundaries.- Profile: display name and image upload/removal.
- Security: contact-only or sign-in email addresses, verification, passwords, email codes, passkeys, independent OAuth sign-in controls, and authenticator enrollment/removal. Removing an email or sign-in method preserves the last usable alternative.
- Notifications: actual project notification categories, including required categories.
- Sessions: review/revoke sessions and sign out other devices.
- Teams: shared selection, incoming invitations, creation, per-team profile, team details, members and roles, outgoing invitations, administration transfer, leaving, and team deletion.
- API keys: creation, expiry, one-time secret display/copy, and revocation.
- Billing: available public offers and hosted checkout, purchases/subscriptions, cancellation and available plan switches, invoices, and optional payment-method updates.
- Account actions: sign-out and project-permitted account deletion with personal-note cleanup.
Controls follow Hexclave's public project settings and team permissions. Unauthorized member, invitation, key, and team-billing data is not queried by the corresponding sections. Hexclave must still authorize every request; UI visibility is not authorization. Team switching never broadens access to personal Convex notes.
Some enabled services require product-specific configuration: an enabled payments app does not create prices or subscriptions, and an enabled API-key app does not authorize an application's own endpoints. Empty accounts and unconfigured data display empty states.
Teams and permissions
The navigation team dropdown, in the desktop sidebar or mobile drawer, and Teams settings read and update the same Hexclave user.selectedTeam. Personal account clears the selection; selecting a membership or accepting an invitation updates it. Creating a team uses the SDK's creation flow, which selects the new team. Settings render details for that selected team rather than keeping a separate selection.
hexclave.config.ts enables client team creation, user/team API keys, and client account deletion. It defines team_member with $read_members and team_admin with the six team system permissions. Creators receive team_admin; joining members receive team_member. The existing team_admin role authorizes promotion, demotion, administration transfer, and recovery of another member’s interrupted update. These are configured defaults, not a migration of existing custom grants.
The connected sections enforce distinct access gates:
| Section or action | Required permission or setting |
|---|---|
| Member list | $read_members |
| Send invitations | $invite_members |
| Outstanding invitations | $read_members and $invite_members |
| Revoke invitations or remove members | $remove_members |
| Team name/image | $update_team |
| Promote, demote, or transfer administration | team_admin |
| Team API keys | Project team-key setting and $manage_api_keys |
| Team billing | team_admin |
| Delete team | $delete_team |
Privileged member/role changes, leaving, and deletion use convex/teamActions.ts. The server derives the actor from authenticated identity, rechecks actual Hexclave membership and permissions, and coordinates writes with durable team-operation locks. The last administrator must transfer administration or delete the team before leaving or deleting their account. Role replacement refuses custom direct grants that need review in Hexclave. An interrupted write keeps its lock; Check interrupted update verifies saved state after the running-action window before allowing another operation. Successful server changes reload the page to refresh the SDK's membership and permission caches. Hexclave remains the authority for teams and permissions.
Account deletion and load recovery
AccountDangerSettings calls convex/accountActions.ts rather than deleting the identity directly in the browser. It checks the live deletion setting and team-administrator constraints, records a pending deletion, deletes the Hexclave identity, then removes that user's personal notes. Pending/deleted records block note reads and writes, including use of an older authenticated token.
A scheduled reconciliation starts after 15 minutes if completion is uncertain. It removes notes only when Hexclave confirms the identity is absent; if the identity remains, it clears the pending state and restores access. A failed lookup preserves notes and schedules another check. Derived products must extend convex/accountData.ts and their data-access policy when they add other user-owned data.
A definite SDK rejection triggers an immediate identity check: a surviving identity restores note access, an absent identity completes cleanup, and a failed confirmation keeps the scheduled recovery in place. Network timeouts remain pending to avoid racing a late successful deletion.
AccountBoundary accepts an optional onRetry callback that must actually refetch the failed data. It remounts the section only after that callback succeeds. Without a supported refresh callback, the error view offers Reload page to clear cached SDK failures and explains that unsaved changes may be lost. Resetting only the React boundary is not a data retry. AccountBoundary.errorFallback may replace its error presentation; the shell header uses a compact popover containing the same real reload recovery.
Offers and payment configuration
/api/account/billing/catalog authenticates the user and requires actual membership plus team_admin for a team catalog. It exposes only eligible user/team offers from the project's synced branch configuration, excluding server-only products and products without a public price. It does not expose the full configuration or server credentials. Environment-only offers are not listed here; hosted checkout resolves current availability, prices, and payment requirements.
Choosing an offer uses the customer SDK's createCheckoutUrl and opens hosted checkout. Already-owned non-stackable offers show as purchased. An empty configured catalog shows an empty state; enabling Payments alone does not create an offer. Hosted checkout works independently of the optional publishable key used for local card updates.
New project setup
First initialize a clean copy of the public template as described in getting started. The public runtime requires no package-read token. Keep product.config.json in product mode and configure the services for this product.
Create the new project's own Hexclave and Convex services, then supply these four variables in an ignored .env.local or secure preview settings:
NEXT_PUBLIC_HEXCLAVE_PROJECT_IDHEXCLAVE_SECRET_SERVER_KEYNEXT_PUBLIC_CONVEX_URLCONVEX_DEPLOY_KEYfor that same development deployment
Run bun install --frozen-lockfile, then keep bun run dev:mode running. Startup uses scripts/sync-convex-env.mjs to copy the public project ID into Convex's HEXCLAVE_PROJECT_ID and the existing HEXCLAVE_SECRET_SERVER_KEY into its server environment. Values are sent through CLI stdin without printing them. It then syncs backend functions and starts Next after the first successful push. The same server key powers authenticated team administration and account deletion; no additional credential is required.
Configure the new application's actual origins as trusted domains in Hexclave, and enable the sign-in providers/features the product needs. Environment values do not create service projects or configure OAuth credentials. Production hosting still needs its own Worker name, account, deployment credentials, and production backend; see DEPLOYMENT.md.
Optional environment configuration:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_PRODUCT_NAME | Brand name override for the name in product.config.json |
NEXT_PUBLIC_PRODUCT_DESCRIPTION | Description override for product.config.json |
HEXCLAVE_EMAIL_THEME_ID | A theme belonging to this Hexclave project, used during configuration sync |
NEXT_PUBLIC_HEXCLAVE_STRIPE_PUBLISHABLE_KEY | The correct Hexclave Payments Stripe platform publishable key for custom card collection |
The payment-method form appears for an existing billing customer only when the optional publishable key is configured. Without it, the billing view explains that card updates are unavailable here. Obtain the correct platform key for the target payment environment; do not substitute a secret key, another Stripe account's key, or a sample key. The installed public Hexclave SDK does not provide this key. Stripe handles card entry inside our dialog. Hosted invoice links open the payment provider's invoice page.
Configuration sync
hexclave.config.ts retains the template's enabled apps and sign-in selections. It contains no project-specific email theme ID. bun run sync:hexclave reads the target cloud branch config first and preserves its existing email theme when HEXCLAVE_EMAIL_THEME_ID is absent. An explicit theme variable overrides that selection. A failed read stops the push. Cloud environment overrides remain service-owned and are not replaced by this script.
The config-sync workflow uses this command and the pinned installed CLI. Its project ID comes from the same GitHub variable as the frontend. Add optional branding/payment variables to GitHub repository variables for deployment, and the optional theme variable for config sync. Do not push configuration blindly with a separate command: the direct CLI push does not apply our theme-preservation step.
Verification
bun run check covers account views and mocked SDK handlers, shared team selection, permission gates, sign-in-method protection, checkout callbacks, and destructive confirmations. Backend tests cover team role rules, account-deletion reconciliation, and personal-note authorization; retry tests exercise the installed SDK's error cache. Configuration tests cover fresh clones, separate project themes, environment branding, and secret-safe backend setup. These tests do not replace live service verification.
Before releasing a derived product, verify hosted authentication and signed-in account pages against its real service projects, including a non-admin member and a new account. Exercise shared team selection, invitations, role transfer, and last-administrator rejection. Test deletion only with a disposable account and verify its application data is removed. Test checkout, card changes, and subscriptions only with the intended payment test environment. Automated fixture tests do not establish that another project's credentials, trusted domains, or billing configuration are correct.