Skip to content

Commit 5a1c142

Browse files
carderneTrigger.dev RepoOps
authored andcommitted
feat(sdk): prefer short-lived run tokens with credential compatibility
Prefer short-lived run tokens for supported SDK operations inside tasks, while preserving explicit credential precedence and environment-key compatibility where needed. - Resolve implicit credentials per request, HTTP retry, page and streaming connection. Missing, malformed, expired or older narrow-scope run tokens select compatibility credentials before the request. Authentication failures never retry with a more privileged key. - Use run tokens for environment/default queries. Project and organization queries select compatibility credentials before sending the request. - Keep same-branch PREVIEW and DEVELOPMENT requests on run tokens. Requests targeting another branch select compatibility credentials. DEVELOPMENT's `default` sentinel targets the root dev environment. STAGING and PRODUCTION ignore branch targeting for credential selection, matching server authentication. - Mint scoped public tokens server-side when using run tokens. Server minting keeps its 30-day maximum, and longer requests fail rather than falling back to root-key signing. Root-key local signing, including scope-less tokens, is unchanged and can still sign longer lifetimes. - AgentChat resolves implicit credentials for every connection, including network reconnects and EOF resubscriptions, and for input appends. Explicit credentials remain explicit. - Send bounded SDK credential-selection metadata through request headers only for implicit in-task selection. The server records internal authentication telemetry; the SDK does not emit rollout counters through customer OpenTelemetry providers. Explicit credentials and calls outside tasks do not emit selection headers. Mono-RevId: cb4c2727cf43e082dc58929b5e4fa10e1d565b75
1 parent 67e834a commit 5a1c142

20 files changed

Lines changed: 2103 additions & 317 deletions
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@trigger.dev/core": patch
3+
"@trigger.dev/sdk": patch
4+
---
5+
6+
Use short-lived run tokens for supported SDK operations inside deployed tasks when available, retaining environment-key compatibility for unsupported operations and missing or expired tokens. Explicit credentials still take precedence. Public-token helpers use server-side minting with run tokens rather than signing locally.
7+
8+
Project and organization queries, and branch overrides targeting another PREVIEW or DEVELOPMENT environment, keep using compatibility credentials. Branch headers do not change credential selection in STAGING or PRODUCTION. Chat subscriptions reread implicit credentials for each connection.

‎packages/core/src/v3/apiClient/core.ts‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { z as z3 } from "zod/v3";
22
import { z } from "zod/v4";
33
import { fromZodError } from "zod-validation-error/v4";
44
import type { RetryOptions } from "../schemas/index.js";
5+
import {
6+
SDK_CREDENTIAL_OPERATION_HEADER,
7+
SDK_CREDENTIAL_REASON_HEADER,
8+
} from "../sdkCredentialTelemetry.js";
59
import { calculateNextRetryDelay } from "../utils/retries.js";
610
import { ApiConnectionError, ApiError, ApiSchemaValidationError } from "./errors.js";
711

@@ -32,6 +36,8 @@ export const defaultRetryOptions = {
3236

3337
export type ZodFetchOptions<TData = any> = {
3438
retry?: RetryOptions;
39+
resolveAccessToken?: () => string;
40+
resolveHeaders?: () => Record<string, string>;
3541
tracer?: TriggerTracer;
3642
name?: string;
3743
attributes?: Attributes;
@@ -254,8 +260,22 @@ async function _doZodFetchWithRetries<TResponseBodySchema extends AnyZodSchema>(
254260
attempt = 1
255261
): Promise<ZodFetchResult<inferZodSchemaOutput<TResponseBodySchema>>> {
256262
try {
263+
let attemptRequestInit = requestInit;
264+
if (options?.resolveHeaders || options?.resolveAccessToken) {
265+
const headers = new Headers(requestInit?.headers);
266+
headers.delete(SDK_CREDENTIAL_OPERATION_HEADER);
267+
headers.delete(SDK_CREDENTIAL_REASON_HEADER);
268+
if (options.resolveHeaders) {
269+
for (const [key, value] of Object.entries(options.resolveHeaders()))
270+
headers.set(key, value);
271+
} else if (options.resolveAccessToken) {
272+
headers.set("Authorization", `Bearer ${options.resolveAccessToken()}`);
273+
}
274+
attemptRequestInit = { ...requestInit, headers };
275+
}
276+
257277
const response = await context.with(suppressTracing(context.active()), () =>
258-
fetch(url, requestInitWithCache(requestInit))
278+
fetch(url, requestInitWithCache(attemptRequestInit))
259279
);
260280

261281
const responseHeaders = createResponseHeaders(response.headers);

0 commit comments

Comments
 (0)