Boot and Config
Episode 3: one config object on CFQ is the SDK's entire contract, and every option maps to one line of resolveConfig or resolveProject
Copy this guide and your current settings as a prompt for your AI agent.
Preview agent promptSee the guide, code and settings included when you copy.
Everything you can tell the Coframe SDK fits in one object pushed onto an
array: config. Boot’s very first act is to wait for it. Push it before the
script loads or after; it is replayed either way. This episode: every option,
and the exact code that reads them.
On this page
01Before any option is read
Two things run first. A kill switch: cf_disable in the URL reveals the page
and stops the SDK cold. Then the wait: waitForCmd(cmd => !!cmd.config). If
it never comes, boot logs a timeout and exits. Everything else happens after
that await.
02The option map
Seventeen fields in five families (CoframeConfig in
sdk/shared/types/coframe.ts):
| Family | Options |
|---|---|
| Identity | projectId (required) |
| Timing | timeoutMs, batchSize, startTime |
| Waits | waitForHydration, waitForCookiesEnabled, waitForFeatureFlag, waitForUserToken: each a gate boot can pause on |
| Behavior | trackingOnly, anti-flicker mode, previewVariantId, currentUrl, userContext, nonce, enableDebug |
| Fossils | enableAntiFlickering (deprecated) and timeoutBehavior (reserved): declared but never read |
The customer-facing reference is Config Reference.
03Clamps and defaults
resolveConfig starts with arithmetic. The timeout is clamped to
min(yours, 5000): you can shorten the watchdog, never lengthen it. Batch
size defaults to 32. Two timestamps are captured before any await:
integration start (the snippet’s clock, from startTime) and execution start
(now). They are taken early because the next gate can block for five full
seconds.
04Graceful exit, quietly
When boot gives up early it calls gracefulExit: a console.warn, reveal the
page, return null. It does not use the logger, and the source comment says
why: no side effects and no monitoring requests while the feature flag or user
token is not set yet. A page that never opted in stays untouched, and
unmeasured.
05The feature-flag gate
waitForFeatureFlag adds one gate. The SDK waits for a command carrying
featureFlagSet, in the flat shape or the deprecated emit wrapper; both
parse. Three outcomes: true, boot continues; false, graceful exit;
silence until the timeout, graceful exit again. Your flag system stays in
charge; the SDK listens for one boolean. This is unrelated to Coframe’s own
Feature Flags product.
06The reveal contract
resolveConfig arms a second, non-blocking wait for variantRendered. Then,
whatever happens, command or timeout, a finally clause calls revealPage
(sdk-js/src/dom.ts): the CSS variables --cf-show and --cf-visibility
flip, the coframe:show event fires, the page appears. Rendering can fail;
the reveal cannot. That is the one-second, up to five-second, valve from the
overview, wired as a finally.
07resolveProject: baked or fetched
Path one, the fast one: the edge baked your whole project into cf.js, and
the bundle pushed it onto CFQ as projectData. findLastCmd reads it
straight off the array. Zero extra round trips. Path two, when there is no
baked data or a preview is kept in session storage: one GET /api/v1/frames
with the config as the query string (projectId, currentUrl,
previewVariantId, userContext, sanitized). An aborted navigation returns
null quietly.
08What rides along
Both paths return two things: the project (frames, experiments, seeds, bins) and the personalization headers: device type, mobile or not, city, country, region, is-bot, bot score, blocklist. They are computed at the edge from the request itself, not in the browser. The SDK carries them into audience checks and onto every session it records.
09Where in the code
| File | What to look for |
|---|---|
sdk/shared/types/coframe.ts |
The full CoframeConfig type |
sdk/sdk-js/src/index.ts |
init(): kill switch, then the five stations |
sdk/sdk-js/src/init/resolveConfig.ts |
TIMEOUT_MAX_MS, gracefulExit, resolveFeatureFlag, the variantRendered wait and .finally(revealPage) |
sdk/sdk-js/src/init/resolveProject.ts |
findLastCmd(c.projectData), fetchProjectFromEdge, personalization headers |
sdk/sdk-js/src/dom.ts |
revealPage |
# Boot and Config Episode 3: one config object on CFQ is the SDK's entire contract, and every option maps to one line of resolveConfig or resolveProject Everything you can tell the Coframe SDK fits in one object pushed onto an array: `config`. Boot’s very first act is to wait for it. Push it before the script loads or after; it is replayed either way. This episode: every option, and the exact code that reads them. [Video: Video: Runtime Walkthrough, episode 3: the kill switch and the wait, the option map, clamps and defaults, graceful exit, the feature-flag gate, the reveal contract, and resolveProject (3m 50s, narrated)](assets/videos/sdk-internals-03-boot-config.mp4) Watch: 3m 50s — Runtime Walkthrough, episode 3: the kill switch and the wait, the option map, clamps and defaults, graceful exit, the feature-flag gate, the reveal contract, and resolveProject ## Before any option is read Two things run first. A kill switch: `cf_disable` in the URL reveals the page and stops the SDK cold. Then the wait: `waitForCmd(cmd => !!cmd.config)`. If it never comes, boot logs a timeout and exits. Everything else happens after that await. ## The option map Seventeen fields in five families (`CoframeConfig` in `sdk/shared/types/coframe.ts`): Family | Options | Identity | `projectId` (required) | Timing | `timeoutMs`, `batchSize`, `startTime` | Waits | `waitForHydration`, `waitForCookiesEnabled`, `waitForFeatureFlag`, `waitForUserToken`: each a gate boot can pause on | Behavior | `trackingOnly`, anti-flicker mode, `previewVariantId`, `currentUrl`, `userContext`, `nonce`, `enableDebug` | Fossils | `enableAntiFlickering` (deprecated) and `timeoutBehavior` (reserved): declared but never read | The customer-facing reference is [Config Reference](../docs/config-reference.html). ## Clamps and defaults `resolveConfig` starts with arithmetic. The timeout is clamped to `min(yours, 5000)`: you can shorten the watchdog, never lengthen it. Batch size defaults to 32. Two timestamps are captured before any await: integration start (the snippet’s clock, from `startTime`) and execution start (now). They are taken early because the next gate can block for five full seconds. ## Graceful exit, quietly When boot gives up early it calls `gracefulExit`: a `console.warn`, reveal the page, return `null`. It does not use the logger, and the source comment says why: no side effects and no monitoring requests while the feature flag or user token is not set yet. A page that never opted in stays untouched, and unmeasured. ## The feature-flag gate `waitForFeatureFlag` adds one gate. The SDK waits for a command carrying `featureFlagSet`, in the flat shape or the deprecated `emit` wrapper; both parse. Three outcomes: `true`, boot continues; `false`, graceful exit; silence until the timeout, graceful exit again. Your flag system stays in charge; the SDK listens for one boolean. This is unrelated to Coframe’s own [Feature Flags](../docs/feature-flags-overview.html) product. ## The reveal contract `resolveConfig` arms a second, non-blocking wait for `variantRendered`. Then, whatever happens, command or timeout, a `finally` clause calls `revealPage` (`sdk-js/src/dom.ts`): the CSS variables `--cf-show` and `--cf-visibility` flip, the `coframe:show` event fires, the page appears. Rendering can fail; the reveal cannot. That is the one-second, up to five-second, valve from the overview, wired as a `finally`. ## resolveProject: baked or fetched Path one, the fast one: the edge baked your whole project into `cf.js`, and the bundle pushed it onto `CFQ` as `projectData`. `findLastCmd` reads it straight off the array. Zero extra round trips. Path two, when there is no baked data or a preview is kept in session storage: one `GET /api/v1/frames` with the config as the query string (`projectId`, `currentUrl`, `previewVariantId`, `userContext`, sanitized). An aborted navigation returns `null` quietly. ## What rides along Both paths return two things: the project (frames, experiments, seeds, bins) and the personalization headers: device type, mobile or not, city, country, region, is-bot, bot score, blocklist. They are computed at the edge from the request itself, not in the browser. The SDK carries them into audience checks and onto every session it records. ## Where in the code File | What to look for | `sdk/shared/types/coframe.ts` | The full `CoframeConfig` type | `sdk/sdk-js/src/index.ts` | `init()`: kill switch, then the five stations | `sdk/sdk-js/src/init/resolveConfig.ts` | `TIMEOUT_MAX_MS`, `gracefulExit`, `resolveFeatureFlag`, the `variantRendered` wait and `.finally(revealPage)` | `sdk/sdk-js/src/init/resolveProject.ts` | `findLastCmd(c.projectData)`, `fetchProjectFromEdge`, personalization headers | `sdk/sdk-js/src/dom.ts` | `revealPage` |