Build Content Hash
Design Log #150 — Build Content Hash
Written for AI agents. See Log Methodology Note below for details.
Background
When deploying to Wix BaaS, we need client (CDN) and server (backend worker) artifacts to stay in sync. Today the build version comes from package.json and doesn't change between deploys unless manually bumped. If a developer rebuilds and redeploys without bumping the version, the CDN and backend can serve mismatched artifacts.
Problem
We need a content-derived identifier that changes whenever the build output changes, regardless of the semver version string. This hash can serve as a deployment cache key to guarantee client/server consistency.
Design
What to hash
All files under the build directory (build/v{version}/) — both frontend/ and backend/ — excluding build-metadata.json itself (since it contains the hash).
Hash algorithm
- SHA-256 over a deterministic stream: sort file paths lexicographically, then for each file feed
relativePath + contentinto the hash - Truncate to first 12 hex characters — short enough for URLs/keys, long enough to avoid collisions in practice
Where to store
The BuildMetadata type already has a sourceHash: string field (currently set to ''). Populate it.
Where to compute
In build-pipeline.ts, after all build artifacts are written (route manifest, server elements, client bundles, public folder copy) but before writing build-metadata.json.
Implementation Plan
Phase 1: Single file change
File: packages/jay-stack/production-server/lib/builder/build-pipeline.ts
Add a
computeBuildHash(dir: string): Promise<string>function:- Recursively collect all file paths under
dir - Filter out
build-metadata.json - Sort paths lexicographically
- Create a SHA-256 hash, update with each file's relative path and content
- Return first 12 hex chars
- Recursively collect all file paths under
Call it at ~line 546 (after
writeRouteManifestand public folder copy, before metadata write):const sourceHash = await computeBuildHash(buildDir);Set
sourceHashin the metadata object (replacing'').
No type changes needed — BuildMetadata.sourceHash already exists.
Verification
jay-stack-cli buildon an example project →build-metadata.jsonhas non-emptysourceHash- Two identical builds → same hash
- Change a source file, rebuild → different hash
Implementation Results
Hash computation — implemented as designed
computeBuildHash()added tobuild-pipeline.ts, called after all artifacts writtensourceHashremoved fromRouteManifest(only lives inBuildMetadata)buildTimestampremoved fromRouteManifest(moved toBuildMetadataonly) to ensure the manifest is deterministic — same source produces same hash
Public folder duplication fix (discovered during implementation)
All Vite build calls in the production pipeline used root: projectRoot without publicDir: false. Vite's default behavior copies public/ into every build output directory. This caused ~190 images duplicated across backend/server/ and every backend/pre-rendered/{route}/ directory.
Fix: added publicDir: false to all 5 Vite build calls (server-code-build, server-element-compile x2, instance-client-build, shared-chunks-build). The build pipeline already explicitly copies public/ to frontend/public/.
Hydration flattening bug fix
Discovered and fixed during this work — see DL#106 "Bug I" for details.
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.