Rename Stack -> Hexclave in examples, app-internal symbols, and crypto docs

- examples: rename the user config module stack.ts/.tsx (and the convex/lovable
  stack/ dirs) to hexclave and update all importers across .ts/.tsx/.jsx; the
  public handler/[...stack] route segment is left unchanged.
- apps/{dashboard,backend,internal-tool}: rename app-local SDK-init symbols
  (stackClientApp -> hexclaveClientApp, getStackServerApp -> getHexclaveServerApp)
  and the dashboard StackCompanion component -> HexclaveCompanion. The public
  StackClientApp/StackServerApp SDK classes are intentionally left unchanged.
- packages/stack-shared: add comments explaining why the crypto/JWT/vault
  "stack-*" literals must NOT be renamed (key derivation / JWKS / KMS-alias
  stability would break existing encrypted data and tokens).

Deliberately excluded: the STACK_* -> HEXCLAVE_* env-var rename (already works via
the dual-read layers; the docker post-build sentinel path is author-deferred), and
all public-contract names (SDK classes, env vars, HTTP headers, handler routes).
This commit is contained in:
Bilal Godil 2026-06-01 17:20:35 -07:00
parent 6fb99c4340
commit 8fc11e1c93
74 changed files with 97 additions and 80 deletions

View File

@ -1,7 +1,7 @@
import { getClickhouseAdminClient } from "@/lib/clickhouse";
import { arePlanLimitsEnforced, getBillingTeamId } from "@/lib/plan-entitlements";
import { findRecentSessionReplay } from "@/lib/session-replays";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { getPrismaClientForTenancy } from "@/prisma-client";
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { KnownErrors } from "@hexclave/shared";
@ -118,7 +118,7 @@ export const POST = createSmartRouteHandler({
const refreshTokenId = auth.refreshTokenId;
const tenancyId = auth.tenancy.id;
const app = getStackServerApp();
const app = getHexclaveServerApp();
const billingTeamId = getBillingTeamId(auth.tenancy.project);
if (billingTeamId != null && arePlanLimitsEnforced()) {

View File

@ -2,7 +2,7 @@ import { getClickhouseExternalClient } from "@/lib/clickhouse";
import { getSafeClickhouseErrorMessage } from "@/lib/clickhouse-errors";
import { arePlanLimitsEnforced, getBillingTeamId } from "@/lib/plan-entitlements";
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { KnownErrors } from "@hexclave/shared";
import { ITEM_IDS, PLAN_LIMITS } from "@hexclave/shared/dist/plans";
import { adaptSchema, adminAuthTypeSchema, jsonSchema, yupBoolean, yupMixed, yupNumber, yupObject, yupRecord, yupString } from "@hexclave/shared/dist/schema-fields";
@ -43,7 +43,7 @@ export const POST = createSmartRouteHandler({
let effectiveTimeoutMs = body.timeout_ms;
const billingTeamId = getBillingTeamId(auth.tenancy.project);
if (billingTeamId != null && arePlanLimitsEnforced()) {
const app = getStackServerApp();
const app = getHexclaveServerApp();
const timeoutItem = await app.getItem({ itemId: ITEM_IDS.analyticsTimeoutSeconds, teamId: billingTeamId });
// clickHouse treats max_execution_time=0 as
// "unlimited", so a customer with zero timeout entitlement (no active

View File

@ -1,7 +1,7 @@
import { isSecureEmailPort, lowLevelSendEmailDirectWithoutRetries } from "@/lib/emails-low-level";
import { arePlanLimitsEnforced, getBillingTeamId } from "@/lib/plan-entitlements";
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { KnownErrors } from "@hexclave/shared";
import { ITEM_IDS } from "@hexclave/shared/dist/plans";
import * as schemaFields from "@hexclave/shared/dist/schema-fields";
@ -51,7 +51,7 @@ export const POST = createSmartRouteHandler({
const billingTeamId = getBillingTeamId(auth.tenancy.project);
const emailItem = billingTeamId == null || !arePlanLimitsEnforced()
? null
: await getStackServerApp().getItem({ itemId: ITEM_IDS.emailsPerMonth, teamId: billingTeamId });
: await getHexclaveServerApp().getItem({ itemId: ITEM_IDS.emailsPerMonth, teamId: billingTeamId });
if (emailItem != null && billingTeamId != null) {
const isDebited = await emailItem.tryDecreaseQuantity(1);
if (!isDebited) {

View File

@ -4,7 +4,7 @@ import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { Prisma } from "@/generated/prisma/client";
import { arePlanLimitsEnforced, getBillingTeamId } from "@/lib/plan-entitlements";
import { findRecentSessionReplay } from "@/lib/session-replays";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { KnownErrors } from "@hexclave/shared";
import { ITEM_IDS } from "@hexclave/shared/dist/plans";
import { adaptSchema, clientOrHigherAuthTypeSchema, yupArray, yupMixed, yupNumber, yupObject, yupString } from "@hexclave/shared/dist/schema-fields";
@ -109,7 +109,7 @@ export const POST = createSmartRouteHandler({
const prisma = await getPrismaClientForTenancy(auth.tenancy);
const recentSession = await findRecentSessionReplay(prisma, { tenancyId, refreshTokenId });
const app = getStackServerApp();
const app = getHexclaveServerApp();
const isNewSession = recentSession == null;
const billingTeamId = getBillingTeamId(auth.tenancy.project);

View File

@ -4,7 +4,7 @@ import { getEmailThemeForThemeId, renderEmailsForTenancyBatched } from "@/lib/em
import { EmailOutboxRecipient, getEmailConfig, } from "@/lib/emails";
import { generateUnsubscribeLink, getNotificationCategoryById, hasNotificationEnabled, listNotificationCategories } from "@/lib/notification-categories";
import { arePlanLimitsEnforced, getBillingTeamId } from "@/lib/plan-entitlements";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { ITEM_IDS } from "@hexclave/shared/dist/plans";
import { getTenancy, Tenancy } from "@/lib/tenancies";
import { getPrismaClientForTenancy, globalPrismaClient, PrismaClientTransaction } from "@/prisma-client";
@ -695,7 +695,7 @@ async function processSingleEmail(context: TenancyProcessingContext, row: EmailO
}
if (context.billingTeamId != null && row.sendRetries === 0 && arePlanLimitsEnforced()) {
const app = getStackServerApp();
const app = getHexclaveServerApp();
const emailItem = await app.getItem({ itemId: ITEM_IDS.emailsPerMonth, teamId: context.billingTeamId });
const isDebited = await emailItem.tryDecreaseQuantity(1);
if (!isDebited) {

View File

@ -1,7 +1,7 @@
import withPostHog from "@/analytics";
import { arePlanLimitsEnforced } from "@/lib/plan-entitlements";
import { globalPrismaClient } from "@/prisma-client";
import { getStackServerApp } from "@/stack";
import { getHexclaveServerApp } from "@/stack";
import { runAsynchronouslyAndWaitUntil } from "@/utils/background-tasks";
import { ITEM_IDS } from "@hexclave/shared/dist/plans";
import { urlSchema, yupBoolean, yupMixed, yupNumber, yupObject, yupString } from "@hexclave/shared/dist/schema-fields";
@ -278,7 +278,7 @@ export async function logEvent<T extends EventType[]>(
const billingTeamId = options.billingTeamId;
if (billingTeamId != null && arePlanLimitsEnforced()) {
const app = getStackServerApp();
const app = getHexclaveServerApp();
const eventsItem = await app.getItem({ itemId: ITEM_IDS.analyticsEvents, teamId: billingTeamId });
const isDebited = await eventsItem.tryDecreaseQuantity(1);
if (!isDebited) {

View File

@ -1,7 +1,7 @@
import { StackServerApp } from '@hexclave/next';
import { getEnvVariable } from '@hexclave/shared/dist/utils/env';
export function getStackServerApp() {
export function getHexclaveServerApp() {
// Fail fast if the backend self-URL env var is missing — without it the SDK
// would silently inherit `defaultBaseUrl` (https://api.stack-auth.com), which
// is almost never what we want for backend self-calls.

View File

@ -5,7 +5,7 @@ async function getServerApp() {
if (isRemoteDevelopmentEnvironmentEnabled()) {
throw new Error("Team invitation management is not available in the remote development environment dashboard.");
}
return (await import("@/stack/server")).getStackServerApp();
return (await import("@/stack/server")).getHexclaveServerApp();
}
export async function revokeInvitation(teamId: string, invitationId: string) {

View File

@ -4,7 +4,7 @@ import { CmdKSearch, CmdKTrigger } from "@/components/cmdk-search";
import { Link } from "@/components/link";
import { Logo } from "@/components/logo";
import { ProjectSwitcher } from "@/components/project-switcher";
import { StackCompanion } from "@/components/stack-companion";
import { HexclaveCompanion } from "@/components/stack-companion";
import ThemeToggle from "@/components/theme-toggle";
import {
Button,
@ -769,7 +769,7 @@ export default function SidebarLayout(props: { children?: React.ReactNode }) {
{/* Stack Companion - overlay with reserved content gutter */}
<div className="pointer-events-none absolute top-0 right-2 bottom-0 z-30 hidden lg:block">
<StackCompanion className="pointer-events-auto" glassBg={isCustomDashboardPage} />
<HexclaveCompanion className="pointer-events-auto" glassBg={isCustomDashboardPage} />
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { getStackServerApp } from "@/stack/server";
import { getHexclaveServerApp } from "@/stack/server";
import { getEnvVariable } from "@hexclave/shared/dist/utils/env";
import { getOrCreateFeaturebaseUser } from "@hexclave/shared/dist/utils/featurebase";
import { urlString } from "@hexclave/shared/dist/utils/urls";
@ -21,7 +21,7 @@ export default async function FeaturebaseSSO({
return <div>Missing return_to parameter. Please go back and try again.</div>;
}
const user = await getStackServerApp().getUser();
const user = await getHexclaveServerApp().getUser();
if (!user) {
redirect(urlString`/handler/sign-in?after_auth_return_to=${urlString`/integrations/featurebase/sso?return_to=${returnTo}`}`);
}

View File

@ -1,4 +1,4 @@
import { getStackServerApp } from "@/stack/server";
import { getHexclaveServerApp } from "@/stack/server";
import { getEnvVariable } from "@hexclave/shared/dist/utils/env";
import { HexclaveAssertionError } from "@hexclave/shared/dist/utils/errors";
import { redirect } from "next/navigation";
@ -18,7 +18,7 @@ export default async function IntegrationConfirmPage(props: {
const onContinue = async (options: { projectId: string, projectName?: string }) => {
"use server";
const user = await getStackServerApp().getUser();
const user = await getHexclaveServerApp().getUser();
if (!user) {
return { error: "unauthorized" };
}

View File

@ -6,7 +6,7 @@ import { SiteLoadingIndicatorDisplay } from "@/components/site-loading-indicator
import { Toaster } from "@/components/ui";
import { VersionAlerter } from "@/components/version-alerter";
import { getPublicEnvVar } from "@/lib/env";
import { stackClientApp } from "@/stack/client";
import { hexclaveClientApp } from "@/stack/client";
import { StackProvider, StackTheme } from "@hexclave/next";
import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
import React, { useSyncExternalStore } from "react";
@ -166,7 +166,7 @@ export function LayoutClient(props: {
}) {
return (
<>
<StackProvider app={stackClientApp} lang={props.translationLocale as React.ComponentProps<typeof StackProvider>["lang"]}>
<StackProvider app={hexclaveClientApp} lang={props.translationLocale as React.ComponentProps<typeof StackProvider>["lang"]}>
<StackTheme>
<ClientPolyfill />
<DevEnvironmentHealthGate>

View File

@ -105,22 +105,22 @@ const CLOSE_THRESHOLD = 100;
const SPLIT_SCREEN_BREAKPOINT = 1000;
// Context for sharing companion state with layout
type StackCompanionContextType = {
type HexclaveCompanionContextType = {
drawerWidth: number,
isSplitScreenMode: boolean,
};
const StackCompanionContext = createContext<StackCompanionContextType>({
const HexclaveCompanionContext = createContext<HexclaveCompanionContextType>({
drawerWidth: 0,
isSplitScreenMode: false,
});
export function useStackCompanion() {
return useContext(StackCompanionContext);
export function useHexclaveCompanion() {
return useContext(HexclaveCompanionContext);
}
export function StackCompanion({ className, glassBg = false }: { className?: string, glassBg?: boolean }) {
export function HexclaveCompanion({ className, glassBg = false }: { className?: string, glassBg?: boolean }) {
const [activeItem, setActiveItem] = useState<string | null>(null);
const [mounted, setMounted] = useState(false);
const [versionCheckResult, setVersionCheckResult] = useState<VersionCheckResult>(null);
@ -524,7 +524,7 @@ export function StackCompanion({ className, glassBg = false }: { className?: str
if (isSplitScreenMode) {
return (
<StackCompanionContext.Provider value={contextValue}>
<HexclaveCompanionContext.Provider value={contextValue}>
<aside
className={cn(
"sticky top-14 h-[calc(100vh-3.5rem)] dark:top-20 dark:h-[calc(100vh-6rem)] mr-3 flex flex-row-reverse items-stretch shrink-0",
@ -553,7 +553,7 @@ export function StackCompanion({ className, glassBg = false }: { className?: str
{/* Handle */}
{handleComponent}
</aside>
</StackCompanionContext.Provider>
</HexclaveCompanionContext.Provider>
);
}
@ -562,7 +562,7 @@ export function StackCompanion({ className, glassBg = false }: { className?: str
const showDrawerContainer = isOpen || isAnimating;
return (
<StackCompanionContext.Provider value={contextValue}>
<HexclaveCompanionContext.Provider value={contextValue}>
{/* Main Container - Fixed Right Edge, Flex Reverse to push handle left */}
<div className={cn("fixed inset-y-0 right-0 z-50 flex flex-row-reverse items-center pointer-events-none", className)}>
@ -584,6 +584,6 @@ export function StackCompanion({ className, glassBg = false }: { className?: str
{/* 2. Stack Companion Handle (Left of Drawer) */}
{handleComponent}
</div>
</StackCompanionContext.Provider>
</HexclaveCompanionContext.Provider>
);
}

View File

@ -39,7 +39,7 @@ export function shouldDisplayVersionResult(
/**
* Common utility function for checking version against Hexclave API
* Used by both VersionAlerter and StackCompanion components
* Used by both VersionAlerter and HexclaveCompanion components
*/
export function checkVersion(
onResult: (result: VersionCheckResult) => void,

View File

@ -10,7 +10,7 @@ if (getPublicEnvVar("NEXT_PUBLIC_STACK_PROJECT_ID") !== "internal") {
const isPreview = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_PREVIEW") === "true";
const isRemoteDevelopmentEnvironment = getPublicEnvVar("NEXT_PUBLIC_STACK_IS_REMOTE_DEVELOPMENT_ENVIRONMENT") === "true";
export const stackClientApp = new StackClientApp({
export const hexclaveClientApp = new StackClientApp({
baseUrl: {
browser: getPublicEnvVar("NEXT_PUBLIC_BROWSER_STACK_API_URL") ?? getPublicEnvVar("NEXT_PUBLIC_STACK_API_URL") ?? throwErr("NEXT_PUBLIC_BROWSER_STACK_API_URL is not set"),
server: getPublicEnvVar("NEXT_PUBLIC_SERVER_STACK_API_URL") ?? getPublicEnvVar("NEXT_PUBLIC_STACK_API_URL") ?? throwErr("NEXT_PUBLIC_SERVER_STACK_API_URL is not set"),

View File

@ -3,18 +3,18 @@ import "server-only";
import { isRemoteDevelopmentEnvironmentEnabled } from "@/lib/remote-development-environment/env";
import { StackServerApp } from "@hexclave/next";
import { HexclaveAssertionError } from "@hexclave/shared/dist/utils/errors";
import { stackClientApp } from "./client";
import { hexclaveClientApp } from "./client";
type InternalServerApp = StackServerApp<true, "internal">;
let _stackServerApp: InternalServerApp | undefined;
export function getStackServerApp(): InternalServerApp {
export function getHexclaveServerApp(): InternalServerApp {
if (!_stackServerApp) {
if (isRemoteDevelopmentEnvironmentEnabled()) {
throw new HexclaveAssertionError("stackServerApp is not available in the local remote development environment dashboard.");
}
_stackServerApp = new StackServerApp({
inheritsFrom: stackClientApp,
inheritsFrom: hexclaveClientApp,
});
}
return _stackServerApp;

View File

@ -2,7 +2,7 @@
import { Suspense } from "react";
import { StackProvider, StackTheme } from "@hexclave/next";
import { stackClientApp } from "../stack";
import { hexclaveClientApp } from "../stack";
import Loading from "./loading";
import "./globals.css";
@ -13,7 +13,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<title>Hexclave MCP Review Tool</title>
</head>
<body>
<StackProvider app={stackClientApp}>
<StackProvider app={hexclaveClientApp}>
<StackTheme>
<Suspense fallback={<Loading />}>
{children}

View File

@ -22,7 +22,7 @@ const publishableClientKey = envOrDevDefault(
);
const apiUrl = envOrDevDefault(process.env.NEXT_PUBLIC_STACK_API_URL, `http://localhost:${portPrefix}02`);
export const stackClientApp = new StackClientApp({
export const hexclaveClientApp = new StackClientApp({
projectId,
publishableClientKey,
tokenStore: "cookie",

View File

@ -1,5 +1,5 @@
const { StackHandler } = require("@hexclave/next");
const { stackServerApp } = require("../../../stack");
const { stackServerApp } = require("../../../hexclave");
function Handler(props) {
return <StackHandler fullPage app={stackServerApp} routeProps={props} />;

View File

@ -1,5 +1,5 @@
const { StackProvider, StackTheme } = require("@hexclave/next");
const { stackServerApp } = require("../stack");
const { stackServerApp } = require("../hexclave");
require("./globals.css");

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "../../../stack/server";
import { stackServerApp } from "../../../hexclave/server";
export default function Handler(props: unknown) {
return <StackHandler fullPage app = { stackServerApp } routeProps = { props } />;

View File

@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { StackProvider, StackTheme } from "@hexclave/next";
import { stackServerApp } from "../stack/server";
import { stackServerApp } from "../hexclave/server";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import ConvexClientProvider from "@/components/ConvexClientProvider";

View File

@ -2,7 +2,7 @@ import Home from "./inner";
import { preloadQuery, preloadedQueryResult } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import { ConvexHttpClient } from "convex/browser";
import { stackServerApp } from "@/stack/server";
import { stackServerApp } from "@/hexclave/server";
export default async function ServerPage() {
const preloaded = await preloadQuery(api.myFunctions.listNumbers, {

View File

@ -1,5 +1,5 @@
import { api } from "@/convex/_generated/api";
import { stackClientApp } from "@/stack/client";
import { stackClientApp } from "@/hexclave/client";
import { ConvexHttpClient, ConvexClient } from "convex/browser";
const convexHttpClient = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

View File

@ -1,6 +1,6 @@
"use client";
import { stackClientApp } from "@/stack/client";
import { stackClientApp } from "@/hexclave/client";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ReactNode } from "react";

View File

@ -1,7 +1,7 @@
"use node"
import { v } from "convex/values";
import { stackServerApp } from "../stack/server";
import { stackServerApp } from "../hexclave/server";
import { action } from "./_generated/server";

View File

@ -1,7 +1,7 @@
import { v } from "convex/values";
import { stackClientApp } from "../stack/client";
import { stackServerApp } from "../stack/server";
import { stackClientApp } from "../hexclave/client";
import { stackServerApp } from "../hexclave/server";
import { action, mutation, query } from "./_generated/server";

View File

@ -1,6 +1,6 @@
import { ServerTeam, ServerUser } from "@hexclave/next";
import { NextResponse } from "next/server";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
export async function POST(request: Request) {
try {
const body = await request.json();

View File

@ -1,4 +1,4 @@
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
import { FallbackTestClient } from "./client";
export default async function FallbackTestPage() {

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
export default function Handler(props) {
return (

View File

@ -3,7 +3,7 @@ import { Metadata } from "next";
import React from "react";
import Header from "src/components/header";
import Provider from "src/components/provider";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
import './global.css';
export const metadata: Metadata = {

View File

@ -1,7 +1,7 @@
import { branchConfigSchema, getConfigOverrideErrors } from "@hexclave/shared/dist/config/schema";
import { ITEM_IDS, PLAN_LIMITS } from "@hexclave/shared/dist/plans";
import { NextResponse } from "next/server";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
function readValidationResult(result: Awaited<ReturnType<typeof getConfigOverrideErrors>>) {
if (result.status === "ok") {

View File

@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);

View File

@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);

View File

@ -1,4 +1,4 @@
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
export default async function ProtectedPage() {
await stackServerApp.getUser({ or: 'redirect' });

View File

@ -1,4 +1,4 @@
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
export default async function Page() {
const user = await stackServerApp.getUser({ or: 'redirect' });

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
export default function Handler(props) {
return <StackHandler fullPage app={stackServerApp} routeProps={props} />;

View File

@ -2,7 +2,7 @@ import { StackProvider } from "@hexclave/next";
import { Metadata } from "next";
import { Inter } from 'next/font/google';
import Provider from "src/components/provider";
import { stackServerApp } from "src/stack";
import { stackServerApp } from "src/hexclave";
import './global.css';
const inter = Inter({ subsets: ['latin'] });

View File

@ -3,7 +3,7 @@ import "server-only";
import Link from 'next/link';
import { revalidatePath } from "next/cache";
import { randomUUID } from "crypto";
import { stackServerApp } from '@/stack';
import { stackServerApp } from '@/hexclave';
import { Product, Shop } from "@/shop";
export default async function EditShop() {

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "../../../stack";
import { stackServerApp } from "../../../hexclave";
export default function Handler(props: any) {
return <StackHandler fullPage app={stackServerApp} routeProps={props} />;

View File

@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { StackProvider, StackTheme } from "@hexclave/next";
import { Inter } from "next/font/google";
import { stackServerApp } from "../stack";
import { stackServerApp } from "../hexclave";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });

View File

@ -1,6 +1,6 @@
import { ServerUser } from "@hexclave/next";
import { Shop } from "@/shop";
import { stackServerApp } from "@/stack";
import { stackServerApp } from "@/hexclave";
export default async function Home() {
const users = await stackServerApp.listUsers();

View File

@ -1,4 +1,4 @@
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
const updateUIState = (user: any | null) => {
const authOptions = document.getElementById("authOptions");

View File

@ -1,4 +1,4 @@
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
// Check if user is already signed in
stackClientApp.getUser().then((user) => {

View File

@ -1,4 +1,4 @@
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
// Check if user is already signed in
stackClientApp.getUser().then((user) => {

View File

@ -1,4 +1,4 @@
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
// Check if user is already signed in
stackClientApp.getUser().then((user) => {

View File

@ -1,4 +1,4 @@
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
// Check if user is already signed in
stackClientApp.getUser().then((user) => {

View File

@ -6,7 +6,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";
import { stackClientApp } from "./stack/client";
import { stackClientApp } from "./hexclave/client";
const queryClient = new QueryClient();

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "../../../stack";
import { stackServerApp } from "../../../hexclave";
export default function Handler(props: any) {
return (

View File

@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { StackProvider, StackTheme } from "@hexclave/next";
import { stackServerApp } from "../stack";
import { stackServerApp } from "../hexclave";
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });

View File

@ -1,4 +1,4 @@
import { stackServerApp } from "@/stack";
import { stackServerApp } from "@/hexclave";
import Link from "next/link";
export default async function Home() {

View File

@ -1,4 +1,4 @@
import { stackServerApp } from "@/stack";
import { stackServerApp } from "@/hexclave";
import Link from "next/link";
export default function Home() {

View File

@ -1,6 +1,6 @@
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { stackServerApp } from './stack';
import { stackServerApp } from './hexclave';
export async function middleware(request: NextRequest) {
console.log('Running middleware on URL: ', request.url);

View File

@ -1,7 +1,7 @@
import { StackHandler, StackProvider, StackTheme } from "@hexclave/react";
import { Suspense } from "react";
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
import { stackClientApp } from "./stack";
import { stackClientApp } from "./hexclave";
function HandlerRoutes() {
const location = useLocation();

View File

@ -1,5 +1,5 @@
import { StackHandler } from "@hexclave/next";
import { stackServerApp } from "../../../stack";
import { stackServerApp } from "../../../hexclave";
export default function Handler(props: any) {
return <StackHandler fullPage app={stackServerApp} routeProps={props} />;

View File

@ -1,5 +1,5 @@
import { StackProvider, StackTheme } from "@hexclave/next";
import { stackServerApp } from "../stack";
import { stackServerApp } from "../hexclave";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (

View File

@ -1,6 +1,6 @@
'use server';
import { stackServerApp } from "@/stack";
import { stackServerApp } from "@/hexclave";
import * as jose from "jose";
/*

View File

@ -6,7 +6,7 @@ import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-r
import type { ReactNode } from "react";
import { Suspense, useMemo } from "react";
import { Header } from "~/components/header";
import { createStackApp } from "~/stack";
import { createStackApp } from "~/hexclave";
export const Route = createRootRoute({
head: () => ({

View File

@ -1,6 +1,9 @@
import { decodeBase64, encodeBase64 } from "../../utils/bytes";
import { decrypt, encrypt, hash, iteratedHash } from "../../utils/crypto";
// NOTE (Hexclave rebrand): do NOT rename these "stack-*" purpose tags. They are mixed into the
// vault's key derivation and value encryption; renaming any of them would make every
// previously-stored vault value undecryptable. Internal constants, never user-visible.
const hashPurpose = "stack-data-vault-client-side-encryption-key-hash";
const encryptionSecretPurpose = "stack-data-vault-client-side-encryption-value-encryption-key-hash";
const encryptionValuePurpose = "stack-data-vault-client-side-encryption-value-encryption-value-encryption";

View File

@ -79,6 +79,9 @@ async function getKmsClient() {
}
async function getOrCreateKekId(): Promise<string> {
// NOTE (Hexclave rebrand): do NOT rename this "stack-*" alias. It is the AWS KMS key alias for
// the existing key-encryption key; renaming it would point at a non-existent key and break
// wrap/unwrap of all stored data keys. Internal infrastructure constant, never user-visible.
const id = "alias/stack-data-vault-server-side-kek";
const kms = await getKmsClient();
try {
@ -123,6 +126,8 @@ export async function encryptWithKms(value: string) {
const { dekBytes, edkBytes } = await genDEK();
try {
const ciphertext = await encrypt({
// NOTE (Hexclave rebrand): do NOT rename this "stack-*" purpose tag — it must match the one
// used in decryptWithKms below, or already-encrypted values become undecryptable.
purpose: "stack-data-vault-server-side-encryption",
secret: dekBytes,
value: new TextEncoder().encode(value),

View File

@ -34,6 +34,9 @@ async function getDerivedSymmetricKey(purpose: string, secret: string | Uint8Arr
name: "HKDF",
salt: toArrayBufferBacked(salt),
hash: "SHA-256",
// NOTE (Hexclave rebrand): do NOT rename this "stack-*" literal. It is a domain-separation
// tag baked into HKDF key derivation; changing it would derive different keys and make all
// previously-encrypted data undecryptable. It is an internal constant, never user-visible.
info: new TextEncoder().encode(JSON.stringify([
"stack-crypto-helper-derived-symmetric-key",
purpose,
@ -126,6 +129,9 @@ export async function iteratedHash(options: HashOptions & { iterations: number }
);
return new Uint8Array(await crypto.subtle.deriveBits({
name: "PBKDF2",
// NOTE (Hexclave rebrand): do NOT rename this "stack-*" literal. It is a domain-separation tag
// mixed into the PBKDF2 salt; changing it would change every hash output and break verification
// of all previously-hashed values. It is an internal constant, never user-visible.
salt: new TextEncoder().encode(JSON.stringify([
"stack-crypto-helper-iterated-hash",
options.purpose,

View File

@ -111,6 +111,9 @@ export async function getPrivateJwks(options: {
}]))
.digest()
);
// NOTE (Hexclave rebrand): do NOT rename these "stack-*" literals. They are hashed into the
// per-audience JWT signing secret and key id (kid); renaming them would rotate every project's
// JWKS and invalidate all already-issued access tokens. Internal constants, never user-visible.
const perAudienceSecret = getHashOfJwkInfo("stack-jwk-audience-secret");
const perAudienceKid = getHashOfJwkInfo("stack-jwk-kid").slice(0, 12);