Dashboard › cli › Distillation
745f0681-445a-4a25-8db7-eacbb32c81f6["lore_tm_v1_O17XUCTyMDlmzEDabUNwZz3WpZ_uteQoEJKIm0hTVWQ","lore_tm_v1_HQbwdXxkgdKFJvsQ_Ew1oGtk89geVoYlYyAQRFxx8j8","lore_tm_v1_eNxG8ECCY20wsY_zeAvprjHFR7yagR6pIeqsVOPAFyY","lore_tm_v1_SkV7-7tNQAEGoZOM-ScknPqtuyA3HJ6e4LdRUuvV8pM","lore_tm_v1_bfMB5-nURjP904IB8T9PpqL79vfk2yUwqF2juu-r-Dg","lore_tm_v1_ninIc9vhXcKvXB-qU0IPsNnQ4b6Mjf_kwI9mBGbKpBw","lore_tm_v1_LE8bvMY6ELUUsoXJfocL_s7M6OvASDI-7AMqwP5C9Xg","lore_tm_v1_i8cfU684JZMtpK702ZGaSDVH87_NCFh-ZhknvD2POso"]
Date: Sep 10, 2026
.sentryclirc files are never consulted by packages/cli/src/lib/env-token-host.ts when capturing an environment-token host, because they have weaker integrity than environment values or sntrys_ token claims.captureEnvTokenHost() resolution order is: 1. normalized sntrys_ token url claim (authoritative and immune to environment injection); 2. normalized SENTRY_HOST then SENTRY_URL for non-sntrys_ tokens; 3. DEFAULT_SENTRY_URL (https://sentry.io).captureEnvTokenHost() is idempotent and captures its pinned host before applySentryCliRcEnvShim can mutate env.SENTRY_URL; standard src/cli.ts::preloadProjectContext ordering is: 1. captureEnvTokenHost(); 2. findProjectRoot; 3. applySentryCliRcEnvShim; 4. getDefaultUrl() fallback.getActiveTokenHost() mirrors getAuthConfig precedence: usable stored OAuth host wins over environment token unless SENTRY_FORCE_ENV_TOKEN is set; otherwise an environment token uses getEnvTokenHost().https://sentry.io may trust strict https default-port *.sentry.io origins; non-SaaS hosts never use suffix/subdomain matching.isSaaSTrustOrigin() requires https: and default port because http://sentry.io is never legitimate and a crafted plaintext URL must NOT inherit SaaS trust.parseSntrysClaim() accepts only sntrys_ tokens of at most 2048 bytes, with exactly 2 underscores, valid base64 JSON payload, and truthy iat plus nonempty url; it optionally returns region_url as regionUrl and org.sntrys_ token format is sntrys_<base64(JSON{iat, url, region_url, org})>_<random-secret> and the standard base64 payload alphabet contains no _, so the second underscore always separates payload from secret.sntrys_ claim URL is authoritative despite not being signed: it is written at server-side issuance and cannot be altered through layered-CI environment injection; a forged token is out of the host-routing threat model because supplying it already compromises/authorizes the credential source.getSdkConfig(regionUrl) strips one trailing slash and returns { baseUrl, fetch: getAuthenticatedFetch(), throwOnError: false as const }; throwOnError is always false because the CLI handles errors itself.getControlSdkConfig() is for endpoints always on the control silo, including OAuth, user accounts, and regions.packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts already contains UX regression coverage showing that a self-hosted sntrys_ token with no SENTRY_HOST/SENTRY_URL causes captureEnvTokenHost() / getEnvTokenHost() to select the token claim origin rather than https://sentry.io.packages/cli/test/lib/security/sntrys-claim-mismatch.test.ts cover: refusing a token whose claim host A is routed by environment to host B without sending its bearer token; allowing matching claim/request origins; allowing registered self-hosted regional silo URLs via isHostTrustedForClaim; allowing opaque non-sntrys_ tokens under ordinary host-scoping; claim precedence over SENTRY_HOST; SaaS fallback for non-sntrys_ tokens without host configuration; and acceptance of a forged claim URL as the captured token scope.packages/cli/test/lib/constants.test.ts covers getConfiguredSentryUrl() behavior: returns undefined with neither env variable; normalizes bare SENTRY_HOST/SENTRY_URL to https://; gives SENTRY_HOST precedence over SENTRY_URL; preserves explicit protocol; and preserves http://localhost:8000 for local development.