-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add isolated RVT connection reader runtime #480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pawellisowski
wants to merge
66
commits into
main
Choose a base branch
from
codex/xeorvt-aware-rvt-reader
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
66 commits
Select commit
Hold shift + click to select a range
241a9f2
docs: harden the local RVT reader boundary
pawellisowski 91d786f
test: define the RVT reader's deterministic boundary
pawellisowski eeb36b3
feat: normalize bounded Revit GLB geometry
pawellisowski 567681e
feat: preserve explicit Revit metadata and relationships
pawellisowski 6032630
feat: fence the local model provider protocol
pawellisowski 2e6f0da
feat: publish crash-safe authenticated model conversions
pawellisowski 48ebace
fix: fence managed model reader lifecycles
pawellisowski 8b0a17e
feat: expose deterministic RVT reference artifacts
pawellisowski 10864f5
feat: publish the local RVT model reader agent
pawellisowski 44b7429
test: keep generated fixtures lint-clean
pawellisowski 65dc08b
fix: close model reader trust and containment gaps
pawellisowski bac867c
fix: reclaim orphaned model cache blobs
pawellisowski 2021b49
fix: coordinate model cache readers and publishers
pawellisowski 2c37ef0
fix: preserve cache trust during maintenance
pawellisowski 7f977fa
fix: bound model reader cancellation and crash recovery
pawellisowski 3a01b74
fix: guarantee model host teardown
pawellisowski 56c8cd4
fix: make model host cleanup cancellation-safe
pawellisowski db9216b
fix: accept real xeoRVT output without weakening trust boundaries
pawellisowski bf873b1
Merge origin/main into xeoRVT reader integration
pawellisowski fe4feba
docs: hand off xeoRVT reader integration
pawellisowski 059f8db
feat: expose authenticated model snapshot packages
pawellisowski 19e2fc7
Merge origin/main into xeoRVT reader integration
pawellisowski 888a2e6
docs: sync model reader command count
pawellisowski c59cfdb
feat: bind managed RVT providers to explicit destinations
pawellisowski cfa3def
feat: bind managed RVT reads to enrolled authority
pawellisowski 2950ce4
Merge origin/main into xeoRVT reader integration
pawellisowski 5ef72ac
Merge origin/main into xeoRVT reader integration
pawellisowski 1665640
feat: preserve provider display metadata provenance
pawellisowski ce845a7
Merge origin/main into xeoRVT reader integration
pawellisowski b6a532c
Merge origin/main into xeoRVT reader integration
pawellisowski 263cd60
Merge remote-tracking branch 'origin/main' into codex/xeorvt-aware-rv…
pawellisowski 28d738a
test(doctor): compare native bridge paths portably
pawellisowski 0617d1a
docs: close the reproducible RVT build boundary
pawellisowski 55c25cb
Merge origin/main into xeoRVT reader integration
pawellisowski 38eb041
build: close the Windows runtime reproducibility boundary
pawellisowski 4b22a6a
build: retain independently verifiable builder authority
pawellisowski b110171
build: exclude ambient Git authority
pawellisowski 2fceaa6
Merge origin/main into xeoRVT reader integration
pawellisowski 1a62cda
fix: bind and close the Windows repro builder authority
pawellisowski e111d1c
fix: address the verified vendored Cargo closure
pawellisowski 25e340f
fix: retain complete reproducible build evidence
pawellisowski 53c78b1
fix: isolate reproducible build cache mutations
pawellisowski 615c7bf
fix: close the reproducible source extraction boundary
pawellisowski ab69de0
fix: make vendored Cargo replacement explicit
pawellisowski 57306f6
fix: bind reader settings into Windows repro authority
pawellisowski b5e2d1c
test: preserve cross-platform reader safety checks
pawellisowski 2e10839
Merge origin/main into xeoRVT reader integration
pawellisowski b8c9073
Merge origin/main so the RVT reader includes current builder fixes
pawellisowski 35e111b
docs: retain current engineering-envelope capability limits in the RV…
pawellisowski 8e2d1a8
fix: remove physical vendor paths from private reproducible builds
pawellisowski e75fa33
fix: bind private Windows builds to complete compiler inputs
pawellisowski 5590f5a
merge: retain upstream lint and Unix process safeguards in RVT
pawellisowski 47f9e2b
fix: preserve compiler provenance when Windows reuses process IDs
pawellisowski f32a3b8
merge: carry npm scanner coverage and dependency cleanup into RVT
pawellisowski 131dddc
fix: avoid debugger heap overhead in sealed compiler builds
pawellisowski 8051c7e
fix: keep Windows runtime builds independent of physical compiler paths
pawellisowski 4591c22
test: make native path controls independent of the Windows code page
pawellisowski 6c89917
Merge origin/main into the RVT reader branch
pawellisowski 0d2a395
fix: refuse a reader schema mismatch before the requested limits can …
pawellisowski e2a1fe4
fix: reject ambient build authority by folded case and case-only coll…
pawellisowski a2054d0
fix: scan every case and escape combination of a forbidden builder root
pawellisowski 676e9ae
fix: bind the v2 property expansion ceiling into the signed shard con…
pawellisowski 2e8e762
fix: bind private compiler paths to their canonical spelling
pawellisowski 0974c69
test: make the forbidden-root regression fail on the platform that ru…
pawellisowski 29050ab
fix(ci): give the first packaged-provider launch room to reach its abort
pawellisowski 96f64e2
Merge origin/main into xeoRVT reader integration
pawellisowski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| #!/usr/bin/env node | ||
| // Fail-closed entrypoint for the private Windows reproducibility proof. Real machine paths live only | ||
| // in the per-builder locator; the canonical manifest and receipt contain logical IDs and digests. | ||
| import { createHash } from 'node:crypto'; | ||
| import { existsSync, readFileSync, realpathSync } from 'node:fs'; | ||
| import { dirname, join, resolve } from 'node:path'; | ||
| import { fileURLToPath } from 'node:url'; | ||
| import { buildConnectionReader, canonicalJson, READER_BUILD_SETTINGS, sha256File } from './build.mjs'; | ||
|
|
||
| const here = dirname(fileURLToPath(import.meta.url)); | ||
| const SHA256 = /^[0-9a-f]{64}$/; | ||
| const POISONED_EXACT = new Set([ | ||
| 'RUSTFLAGS', 'CARGO_ENCODED_RUSTFLAGS', 'CC', 'CFLAGS', 'CL', 'LINK', 'LIB', 'INCLUDE', | ||
| 'NODE_OPTIONS', 'ESBUILD_BINARY_PATH', 'GOOGLE_CLIENT_SECRET', 'AWARE_GOOGLE_CLIENT_SECRET', | ||
| ]); | ||
| const POISONED_PREFIXES = ['npm_config_', 'DOTNET_', 'COREHOST_']; | ||
|
|
||
| const digestText = (text) => createHash('sha256').update(text).digest('hex'); | ||
| const readJson = (path) => JSON.parse(readFileSync(path, 'utf8')); | ||
|
|
||
| export function rejectedAmbientKeys(env) { | ||
| // Windows environment names are case-insensitive, so an ambient | ||
| // `esbuild_binary_path` still resolves as ESBUILD_BINARY_PATH. Match the outer | ||
| // builder: fold case before the exact lookup, and reject names that collide | ||
| // only by case, since which one a child reads is not ours to decide. | ||
| const counts = Object.keys(env).reduce( | ||
| (map, key) => map.set(key.toLowerCase(), (map.get(key.toLowerCase()) ?? 0) + 1), new Map()); | ||
| return Object.keys(env).filter((key) => counts.get(key.toLowerCase()) > 1 | ||
| || POISONED_EXACT.has(key.toUpperCase()) | ||
| || POISONED_PREFIXES.some((prefix) => key.toLowerCase().startsWith(prefix.toLowerCase()))) | ||
| .sort(); | ||
| } | ||
|
|
||
| function verifyRecord(id, record, locator) { | ||
| if (!record || record.id !== id || !SHA256.test(record.sha256 ?? '')) throw new Error(`invalid tool record ${id}`); | ||
| const path = locator?.tools?.[id]; | ||
| if (typeof path !== 'string' || !existsSync(path)) throw new Error(`missing local path for tool ${id}`); | ||
| const actual = sha256File(path); | ||
| if (actual !== record.sha256) throw new Error(`tool digest mismatch for ${id}: ${actual}`); | ||
| return resolve(path); | ||
| } | ||
|
|
||
| export function verifyInternalInputs({ manifest, locator, env = process.env }) { | ||
| if (manifest?.schema !== 'aware-windows-repro-builder/v1') throw new Error('unsupported builder manifest schema'); | ||
| if (locator?.schema !== 'aware-windows-repro-locator/v1') throw new Error('unsupported builder locator schema'); | ||
| const poison = rejectedAmbientKeys(env); | ||
| if (poison.length) throw new Error(`ambient build authority is forbidden: ${poison.join(', ')}`); | ||
| if (manifest.platform !== 'win32' || manifest.arch !== 'x64' || manifest.nodeVersion !== '24.14.0') { | ||
| throw new Error('builder manifest must pin Windows x64 and Node 24.14.0'); | ||
| } | ||
| if (canonicalJson(manifest.settings) !== canonicalJson(READER_BUILD_SETTINGS)) { | ||
| throw new Error('reader build settings differ from the closed implementation'); | ||
| } | ||
| const paths = Object.fromEntries(['node', 'postject', 'web-ifc-wasm'].map((id) => [id, | ||
| verifyRecord(id, manifest.tools?.[id], locator), | ||
| ])); | ||
| if (process.platform !== 'win32' || process.arch !== 'x64' || process.versions.node !== manifest.nodeVersion) { | ||
| throw new Error(`running Node must be exactly ${manifest.nodeVersion} on Windows x64`); | ||
| } | ||
| if (realpathSync(paths.node) !== realpathSync(process.execPath)) throw new Error('the verified Node is not the running Node'); | ||
| for (const [name, path] of Object.entries({ | ||
| 'reader-package-lock': join(here, 'package-lock.json'), | ||
| 'aware-cargo-lock': join(here, '..', 'cli', 'Cargo.lock'), | ||
| })) { | ||
| const expected = manifest.inputs?.[name]; | ||
| if (!SHA256.test(expected ?? '') || sha256File(path) !== expected) throw new Error(`input digest mismatch for ${name}`); | ||
| } | ||
| if (!SHA256.test(manifest.source?.bundleSha256 ?? '') || !/^[0-9a-f]{40}$/.test(manifest.source?.commit ?? '') | ||
| || !/^[0-9a-f]{40}$/.test(manifest.source?.tree ?? '')) throw new Error('invalid source identity in builder manifest'); | ||
| return paths; | ||
| } | ||
|
|
||
| function parseArgs(argv) { | ||
| const out = {}; | ||
| for (let i = 0; i < argv.length; i += 2) { | ||
| if (!argv[i]?.startsWith('--') || argv[i + 1] == null) throw new Error('arguments are --manifest FILE --locator FILE --output DIR'); | ||
| out[argv[i].slice(2)] = argv[i + 1]; | ||
| } | ||
| if (!out.manifest || !out.locator || !out.output) throw new Error('arguments are --manifest FILE --locator FILE --output DIR'); | ||
| return out; | ||
| } | ||
|
|
||
| export async function runInternalReaderBuild({ manifestPath, locatorPath, outputDir, env = process.env }) { | ||
| const manifestText = canonicalJson(readJson(manifestPath)); | ||
| const manifest = JSON.parse(manifestText); | ||
| const locator = readJson(locatorPath); | ||
| const paths = verifyInternalInputs({ manifest, locator, env }); | ||
| const receipt = { | ||
| schema: 'aware-connection-reader-build-receipt/v1', | ||
| buildId: digestText(manifestText), | ||
| builderManifestSha256: digestText(manifestText), | ||
| source: manifest.source, | ||
| inputs: manifest.inputs, | ||
| settings: manifest.settings, | ||
| tools: Object.fromEntries(Object.entries(manifest.tools).map(([id, record]) => [id, { | ||
| id: record.id, sha256: record.sha256, | ||
| }])), | ||
| commands: { | ||
| bundle: '<esbuild> model-dispatcher.mjs -> <output>/bundle.cjs', | ||
| sea: '<node> --experimental-sea-config sea-config.json [cwd=<output>]', | ||
| inject: '<node> <postject> aware-connection-reader.exe NODE_SEA_BLOB sea-prep.blob [cwd=<output>]', | ||
| }, | ||
| rootTokens: ['<source>', '<output>', '<toolchain>', '<cache>', '<profile>'], | ||
| }; | ||
| return buildConnectionReader({ | ||
| outputDir, nodePath: paths.node, postjectPath: paths.postject, | ||
| wasmPath: paths['web-ifc-wasm'], receipt, verifiedExternalTools: true, | ||
| }); | ||
| } | ||
|
|
||
| if (realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) { | ||
| const args = parseArgs(process.argv.slice(2)); | ||
| await runInternalReaderBuild({ manifestPath: args.manifest, locatorPath: args.locator, outputDir: args.output }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import assert from 'node:assert/strict'; | ||
| import { createHash } from 'node:crypto'; | ||
| import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs'; | ||
| import { tmpdir } from 'node:os'; | ||
| import { join } from 'node:path'; | ||
| import test from 'node:test'; | ||
| import { buildConnectionReader, canonicalJson, READER_BUILD_SETTINGS } from './build.mjs'; | ||
| import { rejectedAmbientKeys, verifyInternalInputs } from './build-internal-repro.mjs'; | ||
|
|
||
| const sha = (bytes) => createHash('sha256').update(bytes).digest('hex'); | ||
|
|
||
| test('SEA configuration is path-independent and every injection operand is relative to output cwd', () => { | ||
| const text = readFileSync(new URL('./build.mjs', import.meta.url), 'utf8'); | ||
| assert.match(text, /main: 'bundle\.cjs'/); | ||
| assert.match(text, /output: 'sea-prep\.blob'/); | ||
| assert.match(text, /cwd: outputDir/); | ||
| assert.match(text, /EXE_NAME, READER_BUILD_SETTINGS\.sea\.section, 'sea-prep\.blob'/); | ||
| assert.match(text, /'--sentinel-fuse', READER_BUILD_SETTINGS\.sea\.sentinelFuse/); | ||
| assert.doesNotMatch(text, /main:\s*join\(outputDir/); | ||
| assert.doesNotMatch(text, /output:\s*join\(outputDir/); | ||
| }); | ||
|
|
||
| test('canonical receipt JSON is independent of insertion order', () => { | ||
| assert.equal(canonicalJson({ z: 1, a: { y: 2, x: 3 } }), canonicalJson({ a: { x: 3, y: 2 }, z: 1 })); | ||
| }); | ||
|
|
||
| test('build cleanup refuses to own the reader source root', async () => { | ||
| const readerRoot = new URL('.', import.meta.url).pathname.replace(/^\/(?:[A-Za-z]:)/, (value) => value.slice(1)); | ||
| await assert.rejects(() => buildConnectionReader({ outputDir: readerRoot }), /must not be the reader source root/); | ||
| }); | ||
|
|
||
| test('ambient compiler, Node, npm, dotnet, and credential authority is rejected', () => { | ||
| assert.deepEqual(rejectedAmbientKeys({ Path: 'ok', RUSTFLAGS: 'poison', npm_config_cache: 'x', DOTNET_ROOT: 'x' }), | ||
| ['DOTNET_ROOT', 'RUSTFLAGS', 'npm_config_cache']); | ||
| assert.deepEqual(rejectedAmbientKeys({ NODE_OPTIONS: '--require evil', AWARE_GOOGLE_CLIENT_SECRET: 'secret' }), | ||
| ['AWARE_GOOGLE_CLIENT_SECRET', 'NODE_OPTIONS']); | ||
| }); | ||
|
|
||
| test('ambient authority is rejected by folded case and by case-only collisions', () => { | ||
| // Windows resolves process.env.ESBUILD_BINARY_PATH from an ambient | ||
| // `esbuild_binary_path`, so a case-sensitive exact match let esbuild run an | ||
| // undeclared binary inside a supposedly closed build. | ||
| assert.deepEqual(rejectedAmbientKeys({ esbuild_binary_path: 'C:\evil.exe' }), ['esbuild_binary_path']); | ||
| assert.deepEqual(rejectedAmbientKeys({ NoDe_OpTiOnS: '--require evil' }), ['NoDe_OpTiOnS']); | ||
| // Two spellings of one name: which one a child reads is not ours to decide. | ||
| assert.deepEqual(rejectedAmbientKeys({ Path: 'a', PATH: 'b' }), ['PATH', 'Path']); | ||
| // A single ordinary Path is still authority the reader needs. | ||
| assert.deepEqual(rejectedAmbientKeys({ Path: 'ok' }), []); | ||
| }); | ||
|
|
||
| test('tool verification goes red when a declared tool byte changes', () => { | ||
| const root = mkdtempSync(join(tmpdir(), 'aware-repro-test-')); | ||
| const tool = join(root, 'tool.bin'); writeFileSync(tool, 'one'); | ||
| const manifest = { | ||
| schema: 'aware-windows-repro-builder/v1', platform: 'win32', arch: 'x64', nodeVersion: '24.14.0', | ||
| settings: READER_BUILD_SETTINGS, | ||
| source: { bundleSha256: 'a'.repeat(64), commit: 'b'.repeat(40), tree: 'c'.repeat(40) }, | ||
| tools: { | ||
| node: { id: 'node', sha256: sha('one') }, | ||
| postject: { id: 'postject', sha256: sha('one') }, | ||
| 'web-ifc-wasm': { id: 'web-ifc-wasm', sha256: sha('one') }, | ||
| }, | ||
| inputs: { 'reader-package-lock': 'd'.repeat(64), 'aware-cargo-lock': 'e'.repeat(64) }, | ||
| }; | ||
| const locator = { schema: 'aware-windows-repro-locator/v1', tools: { node: tool, postject: tool, 'web-ifc-wasm': tool } }; | ||
| writeFileSync(tool, 'two'); | ||
| assert.throws(() => verifyInternalInputs({ manifest, locator, env: {} }), /tool digest mismatch/); | ||
| }); | ||
|
|
||
| test('controlled reader rejects omitted or changed byte-affecting settings', () => { | ||
| const root = mkdtempSync(join(tmpdir(), 'aware-repro-settings-')); | ||
| const tool = join(root, 'tool.bin'); writeFileSync(tool, 'one'); | ||
| const manifest = { | ||
| schema: 'aware-windows-repro-builder/v1', platform: 'win32', arch: 'x64', nodeVersion: '24.14.0', | ||
| source: { bundleSha256: 'a'.repeat(64), commit: 'b'.repeat(40), tree: 'c'.repeat(40) }, | ||
| tools: Object.fromEntries(['node', 'postject', 'web-ifc-wasm'].map((id) => [id, { id, sha256: sha('one') }])), | ||
| inputs: { 'reader-package-lock': 'd'.repeat(64), 'aware-cargo-lock': 'e'.repeat(64) }, | ||
| }; | ||
| const locator = { schema: 'aware-windows-repro-locator/v1', tools: { node: tool, postject: tool, 'web-ifc-wasm': tool } }; | ||
| assert.throws(() => verifyInternalInputs({ manifest, locator, env: {} }), /reader build settings differ/); | ||
| assert.throws(() => verifyInternalInputs({ | ||
| manifest: { ...manifest, settings: { ...READER_BUILD_SETTINGS, sea: { ...READER_BUILD_SETTINGS.sea, section: 'evil' } } }, | ||
| locator, env: {}, | ||
| }), /reader build settings differ/); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
bridgeCI job runsnode --testonubuntu-latest, but this test calls a Windows-only build function and expects a later validation error;buildConnectionReaderinstead immediately throwsaware-connection-reader SEA build requires Windows x64. The tool-mutation test later in this file has the same ordering problem, so this commit adds two deterministic CI failures on Linux. Skip these tests outside Windows or make platform/version checks injectable so the intended validation paths can be tested portably.Useful? React with 👍 / 👎.