Skip to content

Technical documentationInternal

For the team and anyone taking the product over. Not linked from the site and not indexed.

Technical documentation pages

Deployment and environment

This page covers how the suite is deployed on Vercel, how a build gets its identity and public origin, every environment variable the code reads, and the deployment-level rules for redirects, search-engine indexing, the landing export, the developer kit and the technical documentation.

Hosting on Vercel

The README's deployment instructions are short: a Vercel project with the Next.js preset, its root at this repository's root. There is no vercel.json on the application branches; everything the platform needs comes from next.config.ts, which is a thin wrapper over content/apps/next-config.ts:

  • env: the build identity and the suite origin (suiteEnv);
  • headers(): the noindex rule, caching for /vendor and /data, each app's cache rules, and the Content-Security-Policy and security headers for workspaces, task pages and object pages (suiteHeaders; see Security and network);
  • rewrites(): each app's rewrites and the stable address of each product page's social image;
  • redirects(): the legacy addresses (below).

Three further details of the Vercel build (16 October 2026, dd30084 and 4c59707):

  • package.json has a vercel-build script, which Vercel runs in place of build; it is npm run build, so the build and its prebuild and postbuild hooks are unchanged.
  • The daily Patent capture (capture-patent.yml) writes vercel.json with {"ignoreCommand": "exit 0"} on the orphan data branch data/exclusivity, so a push of captured data never starts a Vercel deployment. Only that branch carries the file.
  • On Vercel the platform can move the prerendered HTML before postbuild runs, so postbuild/reference-routes.mjs and postbuild/tasks-routes.mjs skip their HTML content checks there with a logged reason; CI's build runs them in full on every push (the fast chain calls check.yml).

.gitignore also lists .surface-runtime/, a local runtime folder an editor agent wrote into the checkout on 16 October 2026; nothing in the build reads it.

turbopack.root and outputFileTracingRoot are set to the project folder so the build never treats a parent folder's lockfile as the workspace root. outputFileTracingIncludes traces docs/technical/**/*.md into the /technical route's function, because those pages are read at request time and are never served as static files.

The deployment steps the README lists:

  1. Set NEXT_PUBLIC_SUITE_ORIGIN once a domain is attached.
  2. Public pages are indexable by default following the CEO's 16 October SEO request; set SUITE_INDEXABLE=0 on a preview that must remain unindexed.
  3. Set NEXT_PUBLIC_DRYBENCH_URL on the consultancy site's Vercel project to the same address, so mbio.tech/apps links here. That variable belongs to the other repository; nothing in this one reads it.

Workspace account mode, Supabase and Stripe are configured separately and stay off unless WORKSPACE_ACCESS_MODE=account is set; docs/workspace-setup.md gives the activation steps and Server and accounts describes the server side.

Build identity

Exports, provenance blocks and About › Version name the build they came from. scripts/lib/build-info.mjs resolves it once per build:

Value Resolved from, in order
Hash NEXT_PUBLIC_BUILD_HASH; the first seven characters of VERCEL_GIT_COMMIT_SHA; git rev-parse --short=7 HEAD in the checkout; "dev"
Time NEXT_PUBLIC_BUILD_AT; otherwise the current time in UTC, to the second

Each candidate hash is trimmed, lower-cased and cut to seven characters, and is skipped unless the result is seven hex digits. An empty NEXT_PUBLIC_BUILD_AT counts as unset. suiteEnv in content/apps/next-config.ts calls assertProductionBuildInfo during phase-production-build, so a production build that resolves to "dev" fails with an error telling you to build from a git checkout or set one of the two variables. The development server is allowed to report "dev".

The resolved values are passed to every bundle as NEXT_PUBLIC_BUILD_HASH and NEXT_PUBLIC_BUILD_AT. src/components/suite/build-info.ts reads them (falling back to "dev" and an empty string).

Suite origin

Every absolute suite address (metadata, the sitemap, social images, export provenance) starts with the origin from suiteOrigin in content/apps/maker.ts:

  1. NEXT_PUBLIC_SUITE_ORIGIN, when set;
  2. https:// plus VERCEL_PROJECT_PRODUCTION_URL, which Vercel sets at build time to the project's production address;
  3. https://drybench.vercel.app.

parseSuiteOrigin reduces the value to scheme://host[:port] and throws unless it is an absolute https: address, or http: on localhost or 127.0.0.1. A mistyped origin therefore fails the build instead of printing broken addresses. VERCEL_PROJECT_PRODUCTION_URL is not a public variable, so suiteEnv resolves the origin at build time and passes it to client bundles as NEXT_PUBLIC_SUITE_ORIGIN. maker.ts reads each variable by its full name because Next only inlines process.env.NAME reads written out in full.

Environment variables

.env.example lists the server variables with comments. .env and .env.* files are gitignored, except .env.example. Never commit values, and never give a server secret a NEXT_PUBLIC_ prefix.

Public, inlined into client bundles

Variable Read in Default and notes
NEXT_PUBLIC_SUITE_ORIGIN content/apps/maker.ts, via content/apps/next-config.ts Vercel production address, else https://drybench.vercel.app; must be https except on localhost
NEXT_PUBLIC_BUILD_HASH scripts/lib/build-info.mjs, src/components/suite/build-info.ts Resolved as above; required in effect when there is no git checkout and no VERCEL_GIT_COMMIT_SHA
NEXT_PUBLIC_BUILD_AT scripts/lib/build-info.mjs, src/components/suite/build-info.ts Build time in UTC

Build settings

Variable Read in Default and notes
SUITE_INDEXABLE content/apps/next-config.ts (noindexRules) Unset or 1: public indexing enabled. 0: every response carries X-Robots-Tag: noindex
SUITE_KIT src/app/(workspace)/dev-kit/page.tsx, src/app/(workspace)/dev-kit/workspace/page.tsx; e2e/smoke/apps.spec.ts Unset: the dev kit answers 404. 1 builds it (SUITE_KIT=1 npm run build)
NODE_ENV content/apps/next-config.ts, src/components/suite/a11y/announce.ts, src/components/suite/a11y/shortcuts/registry.ts, src/lib/handoff/links.ts, src/lib/literature/*, src/proxy.ts Set by Next. In development the CSP allows eval for React's tools and some development-only warnings run

Set by the platform

Variable Read in Effect
VERCEL src/lib/workspace/server-config.ts, src/lib/literature/sources.ts, src/lib/literature/extraction-access.ts, src/lib/literature/search/quota.ts, scripts/postbuild/tasks-routes.mjs Marks a hosted deployment: disables the local development bypasses and the local literature allowances; the postbuild check looks for HTML under .vercel/output
VERCEL_GIT_COMMIT_SHA scripts/lib/build-info.mjs Second source of the build hash
VERCEL_PROJECT_PRODUCTION_URL content/apps/maker.ts Second source of the suite origin

Server only, read at request time

All of these are optional. With none set, the suite runs in browser mode with the literature features that need no keys.

Variable Read in Default and notes
WORKSPACE_ACCESS_MODE src/lib/workspace/server-config.ts, src/lib/literature/extraction-access.ts, src/lib/literature/search/quota.ts, src/app/api/literature/search/route.ts browser. Only account enables accounts; credentials alone never do
WORKSPACE_SITE_URL src/lib/workspace/server-config.ts, src/lib/literature/extraction-access.ts, src/lib/literature/sources.ts, src/lib/literature/search/quota.ts Unset. The exact https origin of the deployment (http allowed on localhost); mutation endpoints refuse other origins
WORKSPACE_DEVELOPMENT src/lib/workspace/server-config.ts Off. 1 enables a local development namespace only when neither VERCEL nor WORKSPACE_SITE_URL is set, and only for loopback requests
SUPABASE_URL src/lib/workspace/server-config.ts Unset
SUPABASE_PUBLISHABLE_KEY src/lib/workspace/server-config.ts Unset
SUPABASE_SERVICE_ROLE_KEY src/lib/workspace/server-config.ts Unset; secret
STRIPE_SECRET_KEY src/lib/workspace/server-config.ts Unset; secret
STRIPE_WEBHOOK_SECRET src/lib/workspace/server-config.ts Unset; secret
STRIPE_WORKSPACE_PRICE_ID src/lib/workspace/server-config.ts Unset. The only price checkout offers
STRIPE_WORKSPACE_LEGACY_PRICE_IDS src/lib/workspace/server-config.ts Empty. Comma-separated earlier price IDs that still grant the entitlement but are never offered
OPENALEX_API_KEY src/app/api/literature/route.ts, src/lib/literature/sources.ts, src/lib/literature/search/providers.ts Unset; sent as a bearer token to OpenAlex when set
ANTHROPIC_API_KEY src/app/api/literature/extract/route.ts, src/app/api/study/summary/route.ts, src/lib/literature/anthropic.ts, src/lib/literature/search/assistant.ts Unset: AI extraction, summaries and search assistance report that they are not configured
ANTHROPIC_WORKSPACE_ID src/lib/literature/anthropic.ts, src/lib/literature/search/assistant.ts Unset; needed only when the key is not scoped to a workspace
ANTHROPIC_LITERATURE_MODEL src/app/api/literature/extract/route.ts, src/app/api/study/summary/route.ts, src/lib/literature/search/assistant.ts EXTRACTION_MODEL in src/lib/literature/extraction.ts
LITERATURE_AI_ENABLED src/lib/literature/extraction-access.ts, src/lib/literature/search/quota.ts Off. Hosted AI features also need 1 here and account mode
LITERATURE_AI_DAILY_REQUESTS src/lib/literature/extraction-access.ts 25; an integer from 1 to 1,000, per process
LITERATURE_SEARCH_SHARED_QUOTA src/lib/literature/search/quota.ts, src/lib/literature/search/assistant.ts Off. Hosted OpenAlex search and AI planning stay closed until it is 1 and the Supabase URL and service key are set
LITERATURE_OPENALEX_DAILY_REQUESTS src/lib/literature/search/quota.ts 500; at most 10,000
LITERATURE_SEARCH_OWNER_DAILY_REQUESTS src/lib/literature/search/quota.ts 100, never above the OpenAlex limit; at most 10,000
LITERATURE_SEARCH_ASSIST_DAILY_REQUESTS src/lib/literature/search/quota.ts 25; at most 1,000
LITERATURE_SEARCH_ASSIST_OWNER_DAILY_REQUESTS src/lib/literature/search/quota.ts 25, never above the assist limit; at most 1,000

A limit outside its range, or not an integer, falls back to its default.

Tests and tooling only

ALLOW_DRAFT_DATA is a builder-worktree policy exception, not an enforced runtime isolation boundary. It must remain unset for CI and merge acceptance; freeze drafts before merging. The independent write-once gate does not read this switch.

Variable Read in Default and notes
ALLOW_DRAFT_DATA src/lib/hosts/golden.test.ts, src/lib/hosts/presets.test.ts, src/lib/seq/plasmid-data.test.ts Unset. Only 1 allows draft-marked Bioreactor golden runs/presets and Plasmid data versions through these tests' no-draft checks in a builder's worktree. Hash and numerical checks still run. Never set for CI or merge acceptance; freeze drafts before merging. No application-runtime effect.
CI playwright.config.ts Set by GitHub Actions: forbidOnly, one retry, two workers, HTML report, no server reuse
E2E_PORT playwright.config.ts 3210; the account-mode server uses the next port
E2E_BASE_URL playwright.config.ts Unset. When set, tests run against that address and no server is started
E2E_ACCOUNT_BASE_URL playwright.config.ts http://127.0.0.1:<E2E_PORT + 1>
PLAYWRIGHT_CHROMIUM playwright.config.ts, e2e/fixtures/protein-explorer/record.mjs /opt/pw-browsers/chromium-1194/chrome-linux/chrome, used only if it exists
PLAYWRIGHT_NODE e2e/fixtures/protein-explorer/record.mjs @playwright/test
HTTPS_PROXY e2e/fixtures/protein-explorer/record.mjs Passed to Chromium as its proxy when recording fixtures

The workflows also set SCARF_ANALYTICS=false to stop a transitive dependency's install telemetry, and the snapshot commands use Node's own NODE_USE_ENV_PROXY=1 behind an HTTPS proxy (see Data pipeline).

Public indexing and preview override

The CEO's 16 October 2026 SEO request supersedes D-0026's default global noindex behaviour. Public landing pages, methods pages, reference workflows, Bench's empty public scaffold (/o) and Literature are indexable. Set SUITE_INDEXABLE=0 for a preview-wide X-Robots-Tag: noindex override. Robots permits crawling and points at the sitemap. Private object, study, named-work, share and ordinary app-workspace metadata retain their own noindex rules. This change does not publish local user data in static HTML. The reference routes and social cards are generated at build time; see Public reference workflows. No deployment or search console submission is performed by building the code.

Legacy redirects

D-0008 moved the suite to the root of its own origin. legacyRedirects in content/redirects.ts generates permanent redirects from the registry, so the old addresses on the maker's site still work once their host is swapped:

Old address New address
/apps /
/apps/docs, /apps/pricing, /apps/security, /apps/regulatory, /apps/changelog the same page at the root
/apps/<slug> the product page (/protein, /molecule and so on)
/apps/<slug>/app and /apps/<slug>/app/<page> the workspace and its sub-pages
/apps/<slug>/opengraph-image the product page's stable social-image address
/apps/docs/<slug> the app's docs page
/tools/<slug> (Protein, Molecule, Pipeline, Cell) the workspace, from legacyPath in content/apps/identity.ts
/tools/inside-a-cell/s/<step> /cell/app?step=<step>, from Cell's own legacyRedirects

<slug> is the registry slug (for example protein-explorer); the destination uses the app's route segment from content/apps/routes.ts. Next passes query strings through, and the browser keeps a #s= fragment across a redirect whose target has none, so a shared link keeps its state.

The landing export for mbio.tech

mbio.tech/apps is a copy of this site's landing page, rendered by the consultancy repository from /landing.json. The route src/app/landing.json/route.ts is static (dynamic = "force-static"), sends Access-Control-Allow-Origin: *, and returns landingExport() from src/app/(marketing)/landing-data.ts, which the landing page reads too. Copy and section changes therefore reach the copy without any work here.

The export carries version: LANDING_EXPORT_VERSION, currently 7 (docs/technical/registry-and-content.md has each version's change). Version 5 (D-0112, 21 October 2026) moves the storage line from trust.storage to workspace.storage, beside Bench, where the page now shows it; sectionOrder followed the page's order of that day (D-0132 of 24 October 2026 reorders its values to hero, guided-example, trust, how-it-works, apps, ai-integration, education without a bump) and the catalogue groups run discovery, bench, then literature (Papers & patents) last. Version 4 added aiIntegration and its section-order entry, and apps.sharedTools plus catalogue copy fields (literatureHeading, literatureDescription, literatureIcon, patentDescriptor). Registered items include Patent through the shared catalogue adapter, while Literature is exported as a shared tool; the hero selection is unchanged. The complete field inventory is in Registry and content. When you add, rename or remove a field, bump the constant and tell the consultancy owner: mbio.tech keeps its last compatible snapshot until its renderer supports the new shape. Its refresh is at most hourly; a refresh alone does not add version-5 support. Version 3 previously replaced the dated Workspace screenshot fields with workspace.preview (D-0038).

The AI section is written as done (D-0134) and shows one figure, the assistant's question and the Calc tool's answer from the worked example's slots; the figure's copy is not exported (24 October 2026). Before D-0134 it described planned hosted connections for assistants such as ChatGPT and Claude, with an explicit not-yet-available caption. Its existing four-tool inventory and setup action are nested under aiIntegration.localPreview; the planned section does not advertise a live connection URL. This is a marketing contract only: no hosted service, authentication integration or deployment configuration is added, and Q-17 remains open.

The developer kit

/dev-kit and /dev-kit/workspace are the primitive gallery and a demonstration workspace. Both pages call notFound() unless SUITE_KIT is 1, so they exist only in builds made with SUITE_KIT=1 npm run build. They are never deployed, never linked, left out of the sitemap and deliberately not named in robots.txt. e2e/smoke/apps.spec.ts expects 200 or 404 according to the variable in the test environment.

Technical documentation

These pages are served at /technical with no name or password (D-0081, 13 October 2026; D-0052 had put them behind HTTP Basic authentication). src/proxy.ts matches /technical and /technical/:path* and sets the area's own security headers from src/lib/technical-docs/headers.ts: no shared caching, noindex, a Content Security Policy with no third-party hosts. The page is rendered on each request and reads only files listed in content/technical-docs.ts; any other address under /technical answers 404. No environment variable is read. TECH_DOCS_USER and TECH_DOCS_PASSWORD, if still set on a deployment, are unused and can be deleted.

The CEO’s homepage MCP marketing request of 18 October 2026 authorizes one narrow exception to D-0081’s public-link restriction: the homepage’s local-preview setup action links to /technical/mcp-server. The same destination is exported in aiIntegration.localPreview.href for the consultancy rendering. This does not open general public navigation to the technical area or change its noindex, sitemap exclusion, caching or security headers. It does not deploy an MCP endpoint: the existing server remains a local repository-run development preview and Q-17 remains open.

See Build, test and CI for how builds are checked before they reach Vercel, and the overview for the rest of this set.