Skip to content

AOT Compile and Runtime

ddddd edited this page Jun 30, 2026 · 3 revisions

AOT Compile and Runtime

Status

This page records the current SPACK compiler/runtime split and the remaining design direction.

As of v2.0.3, SPACK publishes separate runtime and compiler artifacts:

  • spack-runtime serves either a normal frontend dist directory or a compiler-produced .spack bundle.
  • spack-compiler performs build-time optimization and writes .spack bundles.
  • Compression and image generation belong to the compiler path, not the production runtime path.
  • The runtime does not require libvips.

Goal

SPACK should compile frontend build output into an immutable, pre-optimized .spack bundle, then serve that bundle with a minimal runtime.

Frontend build output
-> spack-compiler compile ./dist -o app.spack
-> spack-compiler verify app.spack
-> spack-runtime serve ./app.spack
-> HTTP static delivery

Direct directory serving remains supported for compatibility and simple deployments:

Frontend build output
-> spack-runtime serve ./dist
-> HTTP static delivery without AOT pre-optimization

The compiler can be heavy. The runtime should be small.

Published artifacts

Artifact Role
spack-compiler Build-time tool. Scans assets, creates variants, writes .spack, verifies and reports.
spack-runtime Production runtime. Loads .spack or serves a local directory, selects response plans, sends payloads.
spack runtime image Debian slim runtime image by default.
spack:alpine runtime image Alpine runtime flavor.
spack-compiler image Debian slim compiler image with libvips runtime dependencies.

Why split compiler and runtime

A static delivery runtime should not carry build-time image and compression machinery in production.

The AOT model moves deterministic work to build time. Production becomes a data plane:

  • no runtime image pipeline
  • no runtime compression pipeline
  • no source watcher for bundles
  • no artifact cache janitor for bundles
  • no repeated bundle decompression per request

.spack file model

Current bundles use zstd compression at the bundle level plus SPACK metadata for runtime loading and verification.

app.spack

bundle header
metadata / index
compressed payload section
verification data

The important runtime contract is not the internal container implementation. The contract is:

  • The compiler writes a deterministic bundle for the same input and configuration.
  • The compiler can verify the bundle before it is shipped.
  • The runtime validates the bundle at startup.
  • The runtime extracts the bundle once during startup into a managed temporary directory.
  • Requests are served from the extracted local files, not by decompressing .spack on every request.

Startup extraction keeps the runtime simple and preserves efficient local file serving behavior. It also avoids keeping the whole bundle payload in memory.

Bundle rules

The compiler must enforce these rules:

  • Entry paths are UTF-8 and normalized with / separators.
  • Absolute paths are rejected.
  • .. path traversal is rejected.
  • Backslash paths are rejected.
  • Symlinks are rejected.
  • SPACK metadata paths are reserved.
  • Entries are sorted for reproducible output.
  • Timestamps are fixed by reproducible mode or SOURCE_DATE_EPOCH.
  • File permissions are normalized.
  • Bundle verification must reject malformed metadata, invalid paths, and truncated payloads.

Runtime index

The runtime should not scan source directories or bundle entries on every request.

The index should contain:

  • String table.
  • Route table.
  • Response table.
  • Header table.
  • Variant table.
  • Blob table.
  • Integrity metadata.

A response points to a payload prepared by the compiler or by the direct directory catalog.

route path
-> route id
-> selected response id
-> selected encoding variant
-> local extracted file or direct source file
-> payload delivery

Pre-optimizations

The compiler can precompute everything that does not depend on a single request.

Route index

The compiler normalizes paths, detects duplicate normalized paths, detects case conflicts, validates SPA fallback targets, and writes route records.

Runtime work becomes a route lookup.

Response plans

The compiler writes response records with:

  • Status code.
  • Content type.
  • Content length.
  • ETag.
  • Last-Modified policy.
  • Cache-Control.
  • Vary.
  • Link headers.
  • Security headers.
  • Payload reference.

Runtime work becomes selecting and writing a prepared response plan.

Compression variants

The compiler generates Brotli, Gzip, and Zstd variants when they meet the configured budget and saving ratio.

The runtime selects variants from Accept-Encoding by table lookup.

Image variants

Image generation belongs in the compiler.

The compiler uses libvips-capable builds/images to generate width and format variants. The runtime does not need libvips.

The common matrix controls are --image.enable, --image.widths, and --image.formats. Release verification and Refine AOT benchmarks forward the same settings through SPACK_IMAGE_ENABLE, SPACK_IMAGE_WIDTHS, and SPACK_IMAGE_FORMATS.

The compiler should enforce:

  • No upscaling.
  • Maximum source bytes.
  • Maximum decoded pixels.
  • Maximum variants per asset.
  • Maximum bundle growth ratio.
  • Minimum saving ratio.
  • Format capability detection.

Blob deduplication

The compiler hashes payloads and stores identical content once when the bundle layout supports it.

Deduplication applies to original files, compression variants, image variants, and duplicated files in the source tree.

Archive layout

The compiler should order hot startup assets close together:

  • index.html.
  • Entry CSS.
  • Entry JavaScript.
  • Vendor JavaScript.
  • Critical fonts.
  • Critical images.

Large downloads, source maps, and cold assets should be placed later.

HTML and manifest analysis

The compiler can analyze index.html, Vite manifests, Webpack manifests, Rspack manifests, and custom asset manifests.

It can validate missing references and generate resource hints.

V1 should prefer Link response headers over rewriting HTML.

Conditional and range metadata

The compiler prepares metadata for ETag, HEAD, 304, Range, 206, and 416 responses.

Range requests should prefer identity payloads and avoid byte-range serving of precompressed variants.

Runtime responsibilities

The runtime should:

  • Open .spack or a local source directory.
  • Validate bundle metadata and bounds at startup.
  • Extract .spack bundles once during startup into a managed temporary directory.
  • Build or load route lookup.
  • Select response plans.
  • Serve local extracted files or direct source files.
  • Support health checks, metrics, logging, and graceful shutdown.

The runtime should not include by default:

  • libvips.
  • Runtime image pipeline.
  • Runtime compression pipeline.
  • Source watcher for bundle mode.
  • Catalog rescan for bundle mode.
  • Artifact janitor for bundle mode.
  • Dynamic artifact cache for bundle mode.
  • Runtime hashing of source files for bundle mode.

Compiler reports and source sidecar trust

From the next release after v2.0.5, spack-compiler compile supports an optional JSON report:

spack-compiler compile ./dist -o app.spack --report compile-report.json

The report records:

  • input asset count
  • variant count
  • source sidecar count
  • source bytes
  • bundle bytes
  • compile duration
  • compression mode

External compression sidecars from frontend build output are treated as untrusted until validated. SPACK validates both the HTTP content coding and, for binary image assets, the decoded payload magic. For example, hero.png.gz must be a valid gzip stream and must decode to PNG bytes before it can be registered as a source sidecar variant. Invalid sidecars are ignored and the runtime falls back to the real source file.

Verification and inspection

spack-compiler verify app.spack should support full verification.

spack-compiler decompile app.spack should support extracting or inspecting bundle content for CI and debugging.

spack-runtime serve app.spack should support fast startup verification.

Verification should cover:

  • Bundle path rules.
  • Metadata format version.
  • Section bounds.
  • Route references.
  • Response references.
  • Blob references.
  • Payload hashes.
  • Bundle hash.

Implementation status

Area Status
Runtime direct directory serving Supported.
Compiler/runtime split Supported.
.spack bundle compilation Supported.
.spack verification Supported.
Bundle startup extraction Supported.
Runtime per-request decompression Not used.
Runtime libvips dependency Not required.
Compiler libvips dependency Required for compiler image.
Manifest analysis and advanced layout optimization Future work.
Signatures, overlays, mmap, and offset sendfile Future work.

Success criteria

The production-ready AOT path should satisfy:

  • spack-compiler compile ./dist -o app.spack is deterministic for the same input and config.
  • spack-compiler verify app.spack passes before deployment.
  • spack-runtime serve ./app.spack serves assets without scanning the source directory.
  • Runtime startup does not run compression or image generation.
  • Requests do not decompress the bundle repeatedly.
  • ETag, HEAD, 304, Range, SPA fallback, and compression variant selection work from prepared metadata.
  • Production runtime does not require libvips.
  • The runtime image is materially smaller than a compiler image.
  • Hot-path performance is not worse than the direct prepared filesystem mode.

Clone this wiki locally