Rename port prefix envvar

This commit is contained in:
Konstantin Wohlwend 2026-05-27 18:09:52 -07:00
parent 5a8e0e5112
commit fa4f25bcdd
12 changed files with 23 additions and 23 deletions

View File

@ -12,7 +12,7 @@ A: `packages/stack-cli/src/lib/init-prompt.ts` re-exports `createInitPrompt` fro
A: Connected accounts live in `ProjectUserOAuthAccount`. Stored refresh tokens are in `OAuthToken` (`oauthAccountId`, `scopes`, `isValid`), and cached access tokens are in `OAuthAccessToken` (`expiresAt`, `scopes`, `isValid`). A null `OAuthAccessToken.expiresAt` means the OAuth provider did not supply an access-token expiry; `retrieveOrRefreshAccessToken` treats null-expiry tokens as candidates and still calls the provider-specific validity check before returning them. If no usable access token exists, it looks for valid refresh tokens with matching scopes and invalidates only those that the provider explicitly rejects.
## Q: Which Hexclave rename compatibility layers should be avoided in PR #1475 follow-ups?
A: Do not keep backwards compatibility for the MCP tool name, cross-domain auth query parameter names, `NEXT_PUBLIC_STACK_PORT_PREFIX`, or a parallel `hexclaveAppInternalsSymbol`. For refresh/access cookies, read both legacy Stack and new Hexclave cookie names, but only write the canonical Hexclave cookies.
A: Do not keep backwards compatibility for the MCP tool name, cross-domain auth query parameter names, `NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX`, or a parallel `hexclaveAppInternalsSymbol`. For refresh/access cookies, read both legacy Stack and new Hexclave cookie names, but only write the canonical Hexclave cookies.
## Q: How should GitHub Contents API request-body assertions be written in Stack Auth tests?
A: Prefer inline snapshots over individual field selectors. For request bodies that contain base64 file content, parse the JSON body, assert it is an object, decode the `content` field back to UTF-8, and snapshot the normalized call object so the test verifies the path, method, headers, branch, message, sha, and rendered file content together.
@ -393,7 +393,7 @@ A: Use `getAuthorizationHeader()`, which returns `Bearer stackauth_<base64(getAu
A: `RequestLike` accepts both `{ headers: { get(name): string | null } }` and `{ headers: Record<string, string | null> }`. Header lookup is case-insensitive for record-style headers, and supports `authorization`, `x-stack-auth`, and `cookie`.
### Q: Which env var should emulator onboarding URLs use for dashboard port?
A: Use `EMULATOR_DASHBOARD_PORT` (default `26700`) or explicit `STACK_LOCAL_EMULATOR_DASHBOARD_URL`. Do not derive emulator URLs from `NEXT_PUBLIC_STACK_PORT_PREFIX`, because that points to the host dev environment ports (e.g. `92xx`) rather than the emulator host-forwarded ports.
A: Use `EMULATOR_DASHBOARD_PORT` (default `26700`) or explicit `STACK_LOCAL_EMULATOR_DASHBOARD_URL`. Do not derive emulator URLs from `NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX`, because that points to the host dev environment ports (e.g. `92xx`) rather than the emulator host-forwarded ports.
### Q: Why does `PATCH /api/v1/internal/projects/current` fail in local emulator when updating only `onboarding_state`?
A: `createOrUpdateProjectWithLegacyConfig` always called `overrideEnvironmentConfigOverride`, even when there were zero config override keys to apply. In local emulator mode, environment config overrides are intentionally blocked, so this threw `Environment configuration overrides cannot be changed in the local emulator` and returned 500. The fix is to skip `overrideEnvironmentConfigOverride` unless `configOverrideOverride` has at least one key.

View File

@ -34,11 +34,11 @@ You should ALWAYS add new E2E tests when you change the API or SDK interface. Ge
Hexclave is a monorepo using Turbo for build orchestration. The main components are:
### Apps (`/apps`)
- **backend** (`/apps/backend`): Next.js API backend running on port `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}02` (defaults to 8102)
- **backend** (`/apps/backend`): Next.js API backend running on port `${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}02` (defaults to 8102)
- Main API routes in `/apps/backend/src/app/api/latest`
- Database models using Prisma
- **dashboard** (`/apps/dashboard`): Admin dashboard on port `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}01` (defaults to 8101)
- **dev-launchpad**: Development portal on port `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}00` (defaults to 8100)
- **dashboard** (`/apps/dashboard`): Admin dashboard on port `${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}01` (defaults to 8101)
- **dev-launchpad**: Development portal on port `${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}00` (defaults to 8100)
- **e2e**: End-to-end tests
### Packages (`/packages`)
@ -76,7 +76,7 @@ To see all development ports, refer to the index.html of `apps/dev-launchpad/pub
- When writing tests, prefer .toMatchInlineSnapshot over other selectors, if possible. You can check (and modify) the snapshot-serializer.ts file to see how the snapshots are formatted and how non-deterministic values are handled.
- Whenever you learn something new, or at the latest right before you call the `Stop` tool, write whatever you learned into the .claude/CLAUDE-KNOWLEDGE.md file, in the Q&A format in there. You will later be able to look up knowledge from there (based on the question you asked).
- Animations: Keep hover/click transitions snappy and fast. Don't delay the action with a pre-transition (e.g. no fade-in when hovering a button) — it makes the UI feel sluggish. Instead, apply transitions after the action, like a smooth fade-out when the hover ends.
- Whenever you make changes in the dashboard, provide the user with a deep link to the dashboard page that you've just changed. Usually, this takes the form of `http://localhost:<whatever-is-in-$NEXT_PUBLIC_STACK_PORT_PREFIX>01/projects/-selector-/...`, although sometimes it's different. If $NEXT_PUBLIC_STACK_PORT_PREFIX is set to 91, 92, or 93, use `a.localhost`, `b.localhost`, and `c.localhost` for the domains, respectively.
- Whenever you make changes in the dashboard, provide the user with a deep link to the dashboard page that you've just changed. Usually, this takes the form of `http://localhost:<whatever-is-in-$NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX>01/projects/-selector-/...`, although sometimes it's different. If $NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX is set to 91, 92, or 93, use `a.localhost`, `b.localhost`, and `c.localhost` for the domains, respectively.
- To update the list of apps available, edit `apps-frontend.tsx` and `apps-config.ts`. When you're tasked to implement a new app or a new page, always check existing apps for inspiration on how you could implement the new app or page.
- NEVER use Next.js dynamic functions if you can avoid them. Instead, prefer using a client component to make sure the page remains static (eg. prefer `usePathname` instead of `await params`).
- Whenever you make backwards-incompatible changes to the config schema, you must update the migration functions in `packages/stack-shared/src/config/schema.ts`!

View File

@ -36,7 +36,7 @@ For any security-related concerns & bug bounties, please email us at [security@h
NOTE: Every line of code should be reviewed by a human BEFORE you submit a PR. DO NOT waste our time by creating and submitting an AI-generated PR.
For vibecoding, it can help to have multiple parallel copies of the codebase open in different windows. For this, you can set the environment variable `NEXT_PUBLIC_STACK_PORT_PREFIX` to a different value (default 81). To do this consistently across all coding agents (Claude Code/Cursor Agent/Codex),we recommend you use `direnv` with a `.envrc` file:
For vibecoding, it can help to have multiple parallel copies of the codebase open in different windows. For this, you can set the environment variable `NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX` to a different value (default 81). To do this consistently across all coding agents (Claude Code/Cursor Agent/Codex),we recommend you use `direnv` with a `.envrc` file:
1. Install `direnv` if you haven't already. On Mac, the easiest way is to install it with Homebrew: `brew install direnv`.
2. Update ALL your shell configs to append the following lines. On most Mac setups, this is `~/.bash_profile`, `~/.bashrc`, `~/.zprofile`, `~/.zshrc`, and `~/.zshenv`.
@ -51,13 +51,13 @@ For vibecoding, it can help to have multiple parallel copies of the codebase ope
```sh
# .envrc
# make sure to install direnv and add it to your shell rc file (e.g. ~/.bashrc or ~/.zshrc)
export NEXT_PUBLIC_STACK_PORT_PREFIX=181
export NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX=181
# with this many processes running, it can be useful to add a custom title to all Node.js processes
# export NODE_OPTIONS="--require=<path-to-the-workspace-folder>/scripts/set-process-title.js $NODE_OPTIONS"
```
When you do this, it is recommended that you give all workspaces a port prefix other than 81, to prevent accidental conflicts when you forgot to make a feature support the $NEXT_PUBLIC_STACK_PORT_PREFIX environment variable. (for example: first workspace at 181, second workspace at 182, etc.)
When you do this, it is recommended that you give all workspaces a port prefix other than 81, to prevent accidental conflicts when you forgot to make a feature support the $NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX environment variable. (for example: first workspace at 181, second workspace at 182, etc.)
Also, the cookies on different ports may conflict with each other. To prevent this, open `a.localhost:18101` and `b.localhost:18201` instead or normal localhost, so the cookies are scoped differently.

View File

@ -20,7 +20,7 @@ describe('validateRedirectUrl', () => {
const stackKeys = [
"NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE",
"NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX",
"NEXT_PUBLIC_STACK_PORT_PREFIX",
"NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX",
] as const;
const hexclaveOf = (name: string) => name.replace("STACK_", "HEXCLAVE_");
const allKeys = [...stackKeys, ...stackKeys.map(hexclaveOf)];
@ -28,7 +28,7 @@ describe('validateRedirectUrl', () => {
const newValues: Record<string, string | undefined> = {
NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE: values.hostedHandlerUrlTemplate,
NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX: values.hostedHandlerDomainSuffix,
NEXT_PUBLIC_STACK_PORT_PREFIX: values.stackPortPrefix,
NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX: values.stackPortPrefix,
};
for (const stackKey of stackKeys) {
newValues[hexclaveOf(stackKey)] = newValues[stackKey];
@ -69,7 +69,7 @@ describe('validateRedirectUrl', () => {
describe('exact domain matching', () => {
it('should implicitly validate hosted handler domains for the project', () => {
withHostedHandlerEnv({
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09/{hostedPath}",
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}",
stackPortPrefix: "92",
}, () => {
const tenancy = createMockTenancy({

View File

@ -9,7 +9,7 @@ export function getHostedHandlerTrustedDomain(projectId: string): string {
projectId,
hostedHandlerDomainSuffix: getProcessEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_DOMAIN_SUFFIX"),
hostedHandlerUrlTemplate: getProcessEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE"),
stackPortPrefix: getEnvVariable("NEXT_PUBLIC_STACK_PORT_PREFIX", "81"),
stackPortPrefix: getEnvVariable("NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX", "81"),
});
}

View File

@ -75,9 +75,9 @@ export NEXT_PUBLIC_BROWSER_STACK_DASHBOARD_URL=${NEXT_PUBLIC_STACK_DASHBOARD_URL
# is STACK_ENV_VAR_SENTINEL_NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX, and the sentinel
# substitution loop below derives the env var name from the sentinel — so this
# MUST export NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX or the sentinel never resolves.
# Accept the legacy NEXT_PUBLIC_STACK_PORT_PREFIX as input for back-compat with
# Accept the legacy NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX as input for back-compat with
# existing self-host configs.
export NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX=${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}}
export NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX=${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}}
PORT_PREFIX=${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX}
export NEXT_PUBLIC_SERVER_STACK_DASHBOARD_URL="http://localhost:${PORT_PREFIX}01"
export NEXT_PUBLIC_BROWSER_STACK_API_URL=${NEXT_PUBLIC_STACK_API_URL}

View File

@ -26,7 +26,7 @@ From the repository root:
pnpm -C docs-mintlify run dev
```
This starts Mintlify in `docs-mintlify` on `http://localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}04` (for example, `http://localhost:8104` with the default prefix).
This starts Mintlify in `docs-mintlify` on `http://localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}04` (for example, `http://localhost:8104` with the default prefix).
From inside `docs-mintlify`, you can also run:

View File

@ -8,7 +8,7 @@ This is the documentation site for Stack Auth, built with [Next.js](https://next
pnpm dev
```
The docs server runs on port `8104` by default (or `${NEXT_PUBLIC_STACK_PORT_PREFIX}04`).
The docs server runs on port `8104` by default (or `${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX}04`).
## Project Structure

View File

@ -15,7 +15,7 @@ const hostedHandlerTemplateProjectIdB = "11111111-1111-4111-8111-111111111111";
function replaceStackPortPrefix(input: string | undefined, stackPortPrefix: string | undefined): string | undefined {
if (input == null) return undefined;
return stackPortPrefix ? input.replace(/\$\{NEXT_PUBLIC_STACK_PORT_PREFIX:-81\}/g, stackPortPrefix) : input;
return stackPortPrefix ? input.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, stackPortPrefix) : input;
}
function getHostedHandlerUrlFromTemplate(template: string, projectId: string, hostedPath: string): string {
@ -232,7 +232,7 @@ import.meta.vitest?.test("validateRedirectUrl trusts implicit hosted handler dom
allowLocalhost: false,
trustedDomains: getImplicitlyTrustedDomainsForProject({
projectId,
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09/{hostedPath}",
hostedHandlerUrlTemplate: "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}",
stackPortPrefix: "92",
}),
})).toBe(true);

View File

@ -173,7 +173,7 @@ describe("handler URL targets", () => {
});
it("uses the full hosted handler URL template when configured", () => {
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE", "http://{projectId}.localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09/{hostedPath}");
vi.stubEnv("NEXT_PUBLIC_STACK_HOSTED_HANDLER_URL_TEMPLATE", "http://{projectId}.localhost:${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}09/{hostedPath}");
vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX", "93");
const urls = resolveHandlerUrls({

View File

@ -188,7 +188,7 @@ Tests use Swift Testing framework against a running backend.
swift test
```
The tests connect to `http://localhost:8102` (or `${NEXT_PUBLIC_STACK_PORT_PREFIX}02`).
The tests connect to `http://localhost:8102` (or `${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX}02`).
## API Reference

View File

@ -6,10 +6,10 @@ import FoundationNetworking
/// Shared test configuration
/// Set environment variables to customize test behavior:
/// - NEXT_PUBLIC_STACK_PORT_PREFIX: Port prefix for backend (default: "81")
/// - NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX: Port prefix for backend (default: "81")
/// - STACK_SKIP_E2E_TESTS: Set to "true" to skip E2E tests
struct TestConfig {
static let portPrefix = ProcessInfo.processInfo.environment["NEXT_PUBLIC_STACK_PORT_PREFIX"] ?? "81"
static let portPrefix = ProcessInfo.processInfo.environment["NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX"] ?? "81"
static let baseUrl = "http://localhost:\(portPrefix)02"
static let skipE2E = ProcessInfo.processInfo.environment["STACK_SKIP_E2E_TESTS"] == "true"