Re-arm the node-dylib ABI handshake: stamp aura-core build identity instead of the frozen crate version #348

Closed
opened 2026-07-26 16:13:23 +02:00 by claude · 1 comment
Collaborator

Motivation

C30 (decided under #296) commits the load seam to refusing a node dylib that was not built against the running engine's aura-core. The shipped handshake checks two stamps: the rustc version (honest — emitted by build.rs, moves with the toolchain) and CORE_VERSION = CARGO_PKG_VERSION (inert — the workspace version is frozen at 0.1.0 under C30's no-semver discipline, so every stale dylib matches it; crates/aura-core/src/project.rs:27, check at crates/aura-runner/src/project.rs:404-410). A dylib built against an older aura-core, same rustc, loads and is trusted at the Rust-ABI tier.

Surfaced by the #296 skeptic pass (scope-fork lens). The hand-bump alternative (bump the version on ABI-relevant change) was rejected in the C30 decision: it re-arms the gate only between bumps — an implicit ABI promise with a judgement gap, exactly the promise class C30 refuses to make.

Problem

Replace the CORE_VERSION equality check with a build-identity fingerprint of aura-core: derived mechanically from the aura-core sources at build time (build.rs, beside the existing AURA_RUSTC_VERSION emission), stamped into the descriptor, compared at load. It fires exactly when aura-core changed — not on unrelated engine commits, and never on judgement. The refusal prose names both fingerprints and the fix (rebuild the node crate against this checkout). Scaffolder template and docs stay untouched — path-dep consumption already rebuilds naturally; this guards the separate-checkout / stale-artifact case.

Test-specifiable: a dylib whose stamped fingerprint differs from the host's is refused with the naming prose; an identical build loads.

refs #296

## Motivation C30 (decided under #296) commits the load seam to refusing a node dylib that was not built against the running engine's aura-core. The shipped handshake checks two stamps: the rustc version (honest — emitted by build.rs, moves with the toolchain) and `CORE_VERSION = CARGO_PKG_VERSION` (inert — the workspace version is frozen at 0.1.0 under C30's no-semver discipline, so every stale dylib matches it; `crates/aura-core/src/project.rs:27`, check at `crates/aura-runner/src/project.rs:404-410`). A dylib built against an older aura-core, same rustc, loads and is trusted at the Rust-ABI tier. Surfaced by the #296 skeptic pass (scope-fork lens). The hand-bump alternative (bump the version on ABI-relevant change) was rejected in the C30 decision: it re-arms the gate only between bumps — an implicit ABI promise with a judgement gap, exactly the promise class C30 refuses to make. ## Problem Replace the CORE_VERSION equality check with a build-identity fingerprint of aura-core: derived mechanically from the aura-core sources at build time (build.rs, beside the existing AURA_RUSTC_VERSION emission), stamped into the descriptor, compared at load. It fires exactly when aura-core changed — not on unrelated engine commits, and never on judgement. The refusal prose names both fingerprints and the fix (rebuild the node crate against this checkout). Scaffolder template and docs stay untouched — path-dep consumption already rebuilds naturally; this guards the separate-checkout / stale-artifact case. Test-specifiable: a dylib whose stamped fingerprint differs from the host's is refused with the naming prose; an identical build loads. refs #296
claude added this to the Safe to embed — a Result-clean library surface under a stated compatibility contract milestone 2026-07-26 16:13:23 +02:00
claude self-assigned this 2026-07-26 16:15:20 +02:00
Author
Collaborator

Shipped (RED-first) + one accepted limit minuted

Landed on this run's branch: AURA_CORE_FINGERPRINT (FNV-1a 64 over aura-core's sorted src/**/*.rs, prefix-free records, cargo:rerun-if-changed=src) replaces the frozen crate version as the load seam's aura-core stamp; validate_c_tier refuses on what: "aura-core build". Headline RED test plus a version-bump-proof twin and an aura-core shape pin (16 hex, never CARGO_PKG_VERSION). Descriptor field renamed to aura_core_fingerprint (engine-internal; C-ABI is field order, not names). Two independent review passes; the second reproduced the baked fingerprint from an independent reimplementation.

Accepted limit (review finding, deliberately not closed here). The fingerprint is source-level identity of aura-core only: a node crate resolves its own lockfile and features, so shared-dependency types riding the Rust tier (e.g. chrono_tz::Tz inside ArgValue::Tz) are not guarded by this stamp — identical fingerprints can span builds linking different dependency versions. Pre-existing exposure (the old version stamp guarded even less), consistent with C30's no-ABI-promise stance: the guarded path is the scaffolded path-dep + same-checkout workflow; cross-checkout builds with divergent lockfiles are outside any promise. Documented in crates/aura-core/build.rs. If a full link-graph identity is ever wanted, that is a new design decision, not a bug in this seam.

## Shipped (RED-first) + one accepted limit minuted Landed on this run's branch: `AURA_CORE_FINGERPRINT` (FNV-1a 64 over aura-core's sorted `src/**/*.rs`, prefix-free records, `cargo:rerun-if-changed=src`) replaces the frozen crate version as the load seam's aura-core stamp; `validate_c_tier` refuses on `what: "aura-core build"`. Headline RED test plus a version-bump-proof twin and an aura-core shape pin (16 hex, never `CARGO_PKG_VERSION`). Descriptor field renamed to `aura_core_fingerprint` (engine-internal; C-ABI is field order, not names). Two independent review passes; the second reproduced the baked fingerprint from an independent reimplementation. **Accepted limit (review finding, deliberately not closed here).** The fingerprint is *source-level* identity of aura-core only: a node crate resolves its own lockfile and features, so shared-dependency types riding the Rust tier (e.g. `chrono_tz::Tz` inside `ArgValue::Tz`) are not guarded by this stamp — identical fingerprints can span builds linking different dependency versions. Pre-existing exposure (the old version stamp guarded even less), consistent with C30's no-ABI-promise stance: the guarded path is the scaffolded path-dep + same-checkout workflow; cross-checkout builds with divergent lockfiles are outside any promise. Documented in `crates/aura-core/build.rs`. If a full link-graph identity is ever wanted, that is a new design decision, not a bug in this seam.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#348