Skip to main content
Version: Development

Build an Angular application and deliver it through LSF

Build the maintained application, publish its exact renderer and browser assets, then observe server rendering, hydration, navigation and a controlled update through a real node. Use the matching alpha.5 source checkout on Linux x86_64 and keep its build outputs together. On Windows, run the commands in WSL2. For an existing Angular CLI build without server rendering, use the static framework guide.

Use the supported profile​

This web delivery workflow uses the Angular builder and Operator CLI. It is separate from the capsule templates supported by latent-dev; the developer frontend does not currently build or publish an Angular SSR package.

The authoring recipe accepts explicit client, server and shared inputs from angular-build.json. It compiles an immutable WebAssembly component for generic execution cells. It does not accept arbitrary Angular CLI projects, npm dependencies, Node built-ins, Express servers, native addons or application lifecycle scripts.

InputExact maintained selection
Angular / TypeScript22.1.7 / 6.0.3
Node / Python24.19.0 / 3.13.5
Rust / Wasm tools1.97.1 / wasm-tools 1.254.0
Authoring / dependency profileangular-ssr-component-v1 / scoped-http-get-v1
npm tool graphRenderer package and lock
Runtime and native preparationBounded generic-cell renderer, enforced T1 admission

Provision the remaining exact tools from the repository toolchain. Use an isolated Linux evaluation environment with Chromium and the locked npm tool tree available. Reserve loopback port 19090 for the runner's owned backend; the workflow refuses an occupied port. Its credentials are public test fixtures for temporary node storage. Do not copy them into an installed node.

Ordinary builds record reproducibility: not-checked, hermetic: false and dependencyCompleteness: declared-inputs-incomplete. Two different application versions are not a reproducibility test. A policy requiring a stronger claim must reject these observations; changing the policy silently is not a remedy.

Follow the source from request to browser​

The complete application is angular-reference-application.

SourceResponsibility
Build configurationSelect explicit files, eight routes and the /offline prerender
Server entrySelect a permitted dependency request, project public data and render an HTTP result
Shared applicationDescribe the hydrated page, transferred public state, counter and navigation
Client entryBootstrap Angular with hydration enabled
Version sourceDisplay the immutable application revision

The server requests a dependency through this closed adapter hook. The broker still checks the installed provider, tenant, publication, binding, grant, deadline and remaining budget before dispatch.

This hook is part of the Angular server application and uses TypeScript. For applications written in other languages, use the client SDK examples, which have selectable Rust, TypeScript, Go, C, Java and C# versions.

Request one scoped HTTP dependency
TypeScript
export async function prepare(request: Request) {
if (request.path === '/data') return {url: 'http://127.0.0.1:19090/message'};
if (request.path === '/slow') return {url: 'http://127.0.0.1:19090/slow'};
if (request.path === '/denied') return {url: 'http://127.0.0.1:19091/message'};
return null;
}

Only the bounded backend message field becomes public transferred data. Authentication headers and other backend fields are excluded by the server's projection. Shared/client sources cannot import server code or its resources. Keep signing material, provider credentials and management tokens outside every selected source and asset.

The client hydrates the server's existing application:

Hydrate the server-rendered Angular application
TypeScript
import {bootstrapApplication, provideClientHydration} from '@angular/platform-browser';
import {provideZonelessChangeDetection} from '@angular/core';
import {App} from '../shared/app.js';

const application = await bootstrapApplication(App, {
providers: [provideZonelessChangeDetection(), provideClientHydration()],
});
await application.whenStable();
document.documentElement.dataset['referenceHydrated'] = 'true';

Home/About navigation updates Angular signals and browser history. It uses the maintained application's navigation implementation, without an Angular Router dependency. Modified clicks retain ordinary browser behavior.

Navigate within the hydrated Angular application
TypeScript
navigate(event: MouseEvent, view: 'main' | 'about') {
if (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
event.preventDefault();
this.view.set(view);
globalThis.history.pushState(null, '', view === 'about' ? '/about' : '/');
}

Build two actual application versions​

From the repository root, use a fresh output path and the repository's locked tools. Cargo's adapter dependencies must already be available: the adapter build uses --locked --offline.

export CARGO_TARGET_DIR="$PWD/target"
npm --prefix examples/renderer-profile ci --ignore-scripts --no-audit --no-fund
cargo build --locked -p latent -p latentd
cargo build --locked --release -p latent-wasmtime --bin latent-aot-compiler
python3 tools/build_angular_reference.py \
--toolchain-root "$PWD/examples/renderer-profile" \
--cli "$PWD/target/debug/latent" \
--cargo-target-dir "$PWD/target" \
--output "$PWD/target/angular-reference-guide-builds"

The builder stages the selected green source and the maintained blue variants/blue.ts version source, then builds each independently. This is the valid change for the walkthrough: the visible revision and the actual browser, renderer, source-snapshot and package identities must differ. No generated package or observation is edited afterward. For your own text change, edit the selected shared template and run the single-application builder into another fresh directory.

Inspect reference-builds.json and each version's build-summary.json, observation.json, inputs/metadata/web-application.json and package/. Record their full hashes. Local build inspection reports trustEvaluated: false and executionAuthorized: false; publisher/builder signatures and runtime permission are separate steps.

Sign, admit, publish and exercise the real node​

The maintained fixture exporter signs those exact build outputs with disposable test authorities. Its protected configuration enforces publisher, builder, SBOM, tenant and native-execution policy. The workflow deliberately tries missing publisher/builder evidence before accepting the proper publication.

LSF_ANGULAR_REFERENCE_BUILDS="$PWD/target/angular-reference-guide-builds" \
LSF_ANGULAR_REFERENCE_FIXTURE_ROOT="$PWD/target/angular-reference-guide-fixture" \
cargo test --locked -p latentd --test phase3_reference_fixture -- \
export_actual_angular_reference_fixtures --exact --ignored --nocapture

python3 tools/run_angular_reference_workflow.py \
--cli "$PWD/target/debug/latent" \
--node "$PWD/target/debug/latentd" \
--compiler "$PWD/target/release/latent-aot-compiler" \
--fixture-root "$PWD/target/angular-reference-guide-fixture" \
--nodejs "$(command -v node)" \
--chrome "$(command -v chromium)" \
--toolchain-root "$PWD/examples/renderer-profile" \
--output "$PWD/target/angular-reference-guide.json"

The workflow owns the temporary node, authenticated CLI profiles, backend and real Chromium processes. Its configuration and scenario contain the complete publication, preparation, deployment and HTTP-trigger commands. For an installed node, use your own protected authorities and the operator CLI contract. The fixture's local signing keys are not a production trust configuration.

Check these observations in the receipt:

Request or actionRequired observation
/ and /aboutInitial HTTP HTML already contains the Angular page; Chromium reuses the captured DOM and navigates without replacing it
Immutable client assetsPublication-qualified paths, exact bytes/media types, conditional 304 and no renderer-store increase
/offlineSupplied prerender through shared assets, without a render activation
/failureDeclared HTTP 422 application failure, separate from a platform trap
/dataOne allowed broker-mediated backend response; only its public message enters HTML
/deniedWrong destination rejected; no hidden request to port 19091
/slowCancellation closes actual owned backend work, then a subsequent request succeeds
/accountPublic access denied; authenticated Alice/Bob remain isolated across renders and browser contexts

The browser uses actual shared ingress. DOM reuse from supplied HTML or a browser route-fulfilment mock is insufficient. Public-origin identity is not a user login. The later authenticated phase uses invoke-only test identities; the browser never receives the operator's management token. Refer to the browser boundary for allowed origin, CSP, cookie and credential behavior. Personalized HTML is private and non-shared; immutable public asset caching does not authorize caching authenticated HTML.

Update, revoke and recover​

The runner keeps a render in flight while changing routing. That request keeps its selected publication/revision; subsequent requests follow the new route. It records real canary observations, promotes blue, revokes it, restarts and rolls back to the retained green generation. Operation IDs and expected revisions make replay/reconciliation explicit. A lost reply is not permission to submit a new mutation with a different identity.

Native preparation uses the enforced T1 compiler path. An in-memory prepared image hit is distinct from an authenticated native-store hit after restart; the receipt must demonstrate the latter without inventing an audit event for the former. The final authenticated browser and restart checks must also pass. Use troubleshooting when any stage fails.

Keep evidence with its source​

Retain the workflow JSON even when it fails. A complete successful receipt must bind the exact source, builds, CLI/node/compiler, toolchain and browser; include all browser, canary, revocation, rollback and restart results; and report clean physical process/resource retirement. Partial browser success cannot certify the whole workflow. The runner removes only its own temporary node/client/peer storage; your explicit build, fixture and receipt paths remain for inspection.

The current guide qualification records the complete source-bound run, while the earlier reference qualification retains its own attempts and identities. Registered excerpts have matching real-node execution records for the guide's tested source. The guide coverage record keeps newcomer review separate from automated execution. Resource and latency claims require the matching developer validation campaign; configured limits and one build's elapsed time are not general performance claims.