[CmdletBinding()]
param(
[ValidateSet('Desktop','Console')][string]$Edition='Desktop',
[Parameter(Mandatory=$true)][string]$ImagePath,
[Parameter(Mandatory=$true)][string]$VerifiedBootResult,
[Parameter(Mandatory=$true)][string]$VerifiedHostResult,
[string]$ToolsRoot,
[string]$Version='dev',
[string]$OutputDirectory
)
$ErrorActionPreference='Stop'
$Edition=if($Edition -eq 'Console'){'Console'}else{'Desktop'}
$taskRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if($Version -cnotmatch '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'){throw 'Version must be a short filename-safe label.'}
# The launcher owns the same mandatory image, kernel, source and desktop gates.
$verified=. (Join-Path $PSScriptRoot 'Open.ps1') -Edition $Edition -ImagePath $ImagePath -VerifiedBootResult $VerifiedBootResult -ToolsRoot $ToolsRoot -ValidateOnly
if($verified.Packaged){throw 'Package a verified repository build, rather than repackaging an existing distribution.'}
if($verified.ImageManifest.Edition -ceq 'Dual'){throw 'Edition packages require their separately verified single-edition images.'}
if($verified.ImageManifest.UsbWorkspace -eq $true -and $verified.ImageManifest.Resolution -cne '0x0') {
throw 'USB packages require automatic monitor resolution; a fixed QEMU test resolution is not a deliverable display policy.'
}
if($verified.ImageManifest.DeploymentPolicy -cne 'SingleEditionOnly' -or $verified.Boot.SingleEditionImageProof.Passed -ne $true) {
throw 'New packages require actual single-system image inspection and a boot proof with no edition selection menu.'
}
# Validation-only dot-sourcing shares validators without opening a guest.
$hostPath=(Resolve-Path -LiteralPath $VerifiedHostResult).Path
$hostProof=Read-IndependentJson $hostPath
if($hostProof.HostOnly -ne $true -or $hostProof.GuestExecution -ne $false -or $hostProof.Passed -ne $true -or !$hostProof.SourceHashes -or !$hostProof.Suites) {
throw 'A passing source-bound host test receipt is required.'
}
Assert-IndependentSourceMap $hostProof.SourceHashes $taskRoot 'host verification'
if(!$hostProof.SourceHashes.PSObject.Properties['Source/XFESystem.NativeKernel/Ps2Decoder.cs'] -or
!$hostProof.SourceHashes.PSObject.Properties['Tests/XFESystem.Platform.Tests/XFESystem.Platform.Tests.csproj']) {
throw 'Host verification must include the independent Platform tests and the production PS/2 decoder.'
}
foreach($entry in $verified.Snapshot.PSObject.Properties) {
$hostEntry=$hostProof.SourceHashes.PSObject.Properties[$entry.Name]
if($hostEntry -and [string]$hostEntry.Value -cne [string]$entry.Value) { throw ('Host and guest source hashes differ: '+$entry.Name) }
}
$hostArtifacts=@()
$suiteNames=@()
foreach($suite in $hostProof.Suites) {
if([string]$suite.Suite -cnotmatch '^[A-Za-z0-9._-]+$' -or $suiteNames -contains $suite.Suite -or $suite.Failed -ne 0 -or [int]$suite.Passed -le 0) {
throw 'Host suite names and successful counters are required.'
}
$suiteNames+=([string]$suite.Suite)
$logPath=(Resolve-Path -LiteralPath $suite.Log).Path
if($suite.LogSha256){Assert-IndependentHash $logPath $suite.LogSha256 ('host log '+$suite.Suite)}
$hostArtifacts+= [pscustomobject]@{Source=$logPath;Destination=('Validation/Host/'+$suite.Suite+'.log');Sha256=(Get-IndependentHash $logPath)}
if($suite.Trx) {
$trxPath=(Resolve-Path -LiteralPath $suite.Trx).Path
if($suite.TrxSha256){Assert-IndependentHash $trxPath $suite.TrxSha256 ('host TRX '+$suite.Suite)}
[xml]$trx=Get-Content -LiteralPath $trxPath -Raw -Encoding UTF8
$counter=$trx.SelectSingleNode("//*[local-name()='Counters']")
if(!$counter -or [int]$counter.failed -ne 0 -or [int]$counter.passed -ne [int]$suite.Passed -or
[int]$counter.executed -ne [int]$counter.passed -or [int]$counter.total -ne [int]$counter.passed) {
throw ('Actual host TRX counters are incomplete or differ: '+$suite.Suite)
}
$hostArtifacts+= [pscustomobject]@{Source=$trxPath;Destination=('Validation/Host/'+$suite.Suite+'.trx');Sha256=(Get-IndependentHash $trxPath)}
} elseif($suite.Receipt -and $suite.ReceiptSha256) {
$nativePath=(Resolve-Path -LiteralPath $suite.Receipt).Path
Assert-IndependentHash $nativePath $suite.ReceiptSha256 ('native host receipt '+$suite.Suite)
$native=Read-IndependentJson $nativePath
if($native.Passed -ne $true -or $native.Failed -ne 0 -or $null -eq $native.ExitCode -or $native.ExitCode -ne 0 -or
[int]$native.PassedCount -ne [int]$suite.Passed) { throw ('Native host test execution did not pass: '+$suite.Suite) }
$hostArtifacts+= [pscustomobject]@{Source=$nativePath;Destination=('Validation/Host/'+$suite.Suite+'.json');Sha256=(Get-IndependentHash $nativePath)}
} else {throw ('Actual TRX or source-bound native execution receipt is required: '+$suite.Suite)}
}
if($suiteNames -notcontains 'Platform' -or $suiteNames -notcontains 'NativeContracts' -or $suiteNames -notcontains 'Network' -or $suiteNames -notcontains 'NativeTls' -or $suiteNames -notcontains 'ConsoleEdition') {
throw 'The final host receipt must include Platform, NativeContracts, Network, NativeTls and ConsoleEdition suites. Network/NativeTls host acceptance does not prove guest HTTPS acceptance.'
}
if($verified.Build.Configuration.OtherDiskAccessEnabled -eq $true -and $suiteNames -notcontains 'Storage') {throw 'Other-disk delivery requires actual Storage filesystem host tests.'}
if($verified.Build.Configuration.MultiprocessorBackend -ceq 'LimineApKernelJobsV1' -and $suiteNames -notcontains 'MultiprocessorEvidenceGuards') {
throw 'Native AP kernel delivery requires the source-bound CPU evidence guard suite.'
}
$licensesRoot=Join-Path $PSScriptRoot 'Licenses'
$licensesPath=Get-IndependentFile $licensesRoot 'Manifest.json'
$licenses=Read-IndependentJson $licensesPath
if($licenses.Schema -cne 'XFESystem.IndependentLicenses.V1' -or $licenses.ContainsCosmos -ne $false -or
$licenses.ExcludesLegacyCollectedGraph -ne $true -or !$licenses.Files -or [int]$licenses.PackageCount -ne @($licenses.Packages).Count) {
throw 'The complete independent application license manifest is required.'
}
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Source/XFESystem.NativeRuntime/Application/packages.lock.json') $licenses.ApplicationLockSha256 'license application lock'
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Tools/IndependentKernel/Runtime/NativeToolchain.json') $licenses.RuntimeToolchainLockSha256 'license runtime toolchain lock'
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Tools/JintCleanupPatch/packages.lock.json') $licenses.JintPatchToolLockSha256 'license Jint patch tool lock'
Assert-IndependentHash (Get-IndependentFile $licensesRoot 'LicenseSources.json') $licenses.SourcesSha256 'license sources manifest'
foreach($binding in @(
@('Tools/TlsEntropyPatch/Packages.lock.json','TlsEntropyPatchToolLockSha256'),
@('Source/XFESystem.Tls/DependencyManifest.json','TlsDependencyManifestSha256'),
@('Source/XFESystem.Tls/Packages.lock.json','TlsProjectLockSha256'),
@('Source/XFESystem.NativeRuntime/Application/Security/Manifest.json','TrustedRootManifestSha256'),
@('Source/XFESystem.NativeRuntime/Application/Security/MozillaRoots.pem','TrustedRootSnapshotSha256')
)) {
$expected=$licenses.PSObject.Properties[$binding[1]]
if(!$expected -or [string]$expected.Value -cnotmatch '^[a-f0-9]{64}$') {throw ('Missing TLS/root license source binding: '+$binding[1])}
Assert-IndependentHash (Get-IndependentFile $taskRoot $binding[0]) ([string]$expected.Value) ('license '+$binding[0])
}
if($licenses.GuestAcceptancePassed -ne $false -or $licenses.NativeTlsAcceptancePassed -ne $false -or $licenses.FullReleaseQualified -ne $false) {
throw 'License collection cannot assert guest TLS acceptance or full release qualification.'
}
$tlsLicense=@($licenses.Packages | Where-Object {$_.Id -ceq 'BouncyCastle.Cryptography'})
if($tlsLicense.Count -ne 1 -or $tlsLicense[0].Version -cne '2.7.0' -or !$tlsLicense[0].LicenseFiles -or
$tlsLicense[0].DependencyManifestSha256 -cne $licenses.TlsDependencyManifestSha256) {
throw 'The pinned BouncyCastle package and full license are required.'
}
$rootLicenseNames=@('Security/MozillaRoots-20260925.pem','Security/MozillaRoots-Manifest.json','Security/MozillaRoots-MPL-2.0.txt')
foreach($required in $rootLicenseNames) {
if(@($licenses.Files | Where-Object {$_.Path -ceq $required}).Count -ne 1) {throw ('Missing trusted root source/license: '+$required)}
}
foreach($binding in @(
@('Security/MozillaRoots-20260925.pem','TrustedRootSnapshotSha256'),
@('Security/MozillaRoots-Manifest.json','TrustedRootManifestSha256')
)) {
$entry=@($licenses.Files | Where-Object {$_.Path -ceq $binding[0]})[0]
if($entry.Sha256 -cne $licenses.PSObject.Properties[$binding[1]].Value) {throw ('Collected trusted root source differs: '+$binding[0])}
}
$licenseNames=@()
foreach($entry in $licenses.Files) {
if($licenseNames -contains $entry.Path) {throw ('Duplicate license path: '+$entry.Path)}
$licenseNames+=([string]$entry.Path)
$path=Get-IndependentFile $licensesRoot $entry.Path
Assert-IndependentHash $path $entry.Sha256 ('license '+$entry.Path)
if((Get-Item -LiteralPath $path).Length -ne $entry.Bytes) {throw ('License length differs: '+$entry.Path)}
}
if(!$OutputDirectory){$OutputDirectory=Join-Path $taskRoot 'dist'}
$outputRoot=[IO.Path]::GetFullPath($OutputDirectory)
[void][IO.Directory]::CreateDirectory($outputRoot)
$storageLabel=if($verified.ImageManifest.UsbWorkspace -eq $true){'USB'}else{'RAM'}
$packageId='XFESystem-'+$Edition+'-Independent-'+$storageLabel+'-'+$Version+'-'+[DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()+'-'+[Guid]::NewGuid().ToString('N').Substring(0,8)
$package=Join-Path $outputRoot $packageId
$zipPath=$package+'.zip'
if((Test-Path -LiteralPath $package) -or (Test-Path -LiteralPath $zipPath)){throw 'Refusing to overwrite an existing package or ZIP.'}
[void][IO.Directory]::CreateDirectory($package)
function Copy-IndependentBoundFile([string]$Source,[string]$Relative,[string]$Expected) {
Assert-IndependentHash $Source $Expected ('package input '+$Relative)
if([IO.Path]::IsPathRooted($Relative)){throw 'Package destination must be relative.'}
$destination=[IO.Path]::GetFullPath((Join-Path $package $Relative))
if(!$destination.StartsWith($package+[IO.Path]::DirectorySeparatorChar,[StringComparison]::OrdinalIgnoreCase)) {throw 'Package destination escaped the output directory.'}
[void][IO.Directory]::CreateDirectory((Split-Path -Parent $destination))
Copy-Item -LiteralPath $Source -Destination $destination
Assert-IndependentHash $destination $Expected ('package copy '+$Relative)
}
try {
Copy-IndependentBoundFile $verified.ImagePath 'System.img' $verified.Boot.ImageSha256
Copy-IndependentBoundFile $verified.ImageManifestPath 'System.img.json' (Get-IndependentHash $verified.ImageManifestPath)
Copy-IndependentBoundFile $verified.KernelPath 'Kernel.elf' $verified.Boot.KernelSha256
Copy-IndependentBoundFile $verified.BootPath 'Validation/Boot.json' (Get-IndependentHash $verified.BootPath)
Copy-IndependentBoundFile $verified.BuildPath 'Validation/BuildReceipt.json' $verified.Boot.BuildReceiptSha256
Copy-IndependentBoundFile $verified.SnapshotPath 'Validation/SourceSnapshot.json' $verified.Boot.SourceSnapshotSha256
Copy-IndependentBoundFile $hostPath 'Validation/Host.json' (Get-IndependentHash $hostPath)
foreach($entry in $verified.Snapshot.PSObject.Properties) {
Copy-IndependentBoundFile (Get-IndependentFile $taskRoot $entry.Name) ('SourceSnapshot/'+$entry.Name) ([string]$entry.Value)
}
foreach($entry in $hostProof.SourceHashes.PSObject.Properties) {
Copy-IndependentBoundFile (Get-IndependentFile $taskRoot $entry.Name) ('HostSourceSnapshot/'+$entry.Name) ([string]$entry.Value)
}
foreach($artifact in $hostArtifacts) {Copy-IndependentBoundFile $artifact.Source $artifact.Destination $artifact.Sha256}
foreach($file in Get-ChildItem -LiteralPath $licensesRoot -File -Recurse) {
$relative=$file.FullName.Substring($licensesRoot.Length+1).Replace('\','/')
Copy-IndependentBoundFile $file.FullName ('Licenses/'+$relative) (Get-IndependentHash $file.FullName)
}
foreach($firmware in @($verified.FirmwareCode,$verified.FirmwareVars)) {
Copy-IndependentBoundFile $firmware ('Firmware/'+[IO.Path]::GetFileName($firmware)) (Get-IndependentHash $firmware)
}
[void][IO.Directory]::CreateDirectory((Join-Path $package 'Tools/IndependentKernel'))
Copy-IndependentBoundFile (Join-Path $PSScriptRoot 'Open.ps1') 'Tools/IndependentKernel/Open.ps1' (Get-IndependentHash (Join-Path $PSScriptRoot 'Open.ps1'))
Copy-IndependentBoundFile (Join-Path $PSScriptRoot 'VerifyMultiDisplay.ps1') 'Tools/IndependentKernel/VerifyMultiDisplay.ps1' (Get-IndependentHash (Join-Path $PSScriptRoot 'VerifyMultiDisplay.ps1'))
Copy-IndependentBoundFile $PSCommandPath 'Tools/IndependentKernel/Package.ps1' (Get-IndependentHash $PSCommandPath)
Copy-IndependentBoundFile (Join-Path $taskRoot 'THIRD_PARTY.md') 'ThirdParty.md' (Get-IndependentHash (Join-Path $taskRoot 'THIRD_PARTY.md'))
Copy-IndependentBoundFile (Join-Path $taskRoot 'docs/DesktopFluidityStandard.md') 'DesktopFluidityStandard.md' (Get-IndependentHash (Join-Path $taskRoot 'docs/DesktopFluidityStandard.md'))
Copy-IndependentBoundFile (Join-Path $taskRoot 'Tools/DesktopFluidity/Standard.json') 'Validation/DesktopFluidityStandard.json' (Get-IndependentHash (Join-Path $taskRoot 'Tools/DesktopFluidity/Standard.json'))
if($verified.BingNativeHttpsPassed -eq $true) {
Copy-IndependentBoundFile $verified.BingEvidencePath 'Validation/Guest/BingEvidence.json' $verified.BingEvidenceSha256
}
if($verified.Boot.GpuDiscoveryEvidence) {
Copy-IndependentBoundFile (Get-IndependentFile (Split-Path -Parent $verified.BootPath) 'GpuDiscoveryEvidence.json') 'Validation/Guest/GpuDiscoveryEvidence.json' $verified.Boot.GpuDiscoveryEvidence.Sha256
}
if($verified.Boot.MultiprocessorEvidence) {
Copy-IndependentBoundFile $verified.Boot.MultiprocessorEvidence.Path 'Validation/MultiprocessorEvidence.json' $verified.Boot.MultiprocessorEvidence.Sha256
}
if($verified.UsbStorageProofPath) {
Copy-IndependentBoundFile $verified.UsbStorageProofPath 'Validation/UsbStorage/Result.json' $verified.Boot.UsbStorageProof.Sha256
foreach($file in $verified.UsbStorageProof.EvidenceFiles) {
Copy-IndependentBoundFile (Get-IndependentFile $verified.UsbStorageProofRoot $file.Path) ('Validation/UsbStorage/'+$file.Path) $file.Sha256
}
}
if($verified.MultiDisplay) {
Copy-IndependentBoundFile $verified.MultiDisplay.Path 'Validation/MultiDisplay/Result.json' $verified.Boot.MultiDisplayProof.Sha256
foreach($file in $verified.MultiDisplay.Proof.EvidenceFiles) {
Copy-IndependentBoundFile (Get-IndependentFile $verified.MultiDisplay.Root $file.Path) ('Validation/MultiDisplay/'+$file.Path) $file.Sha256
}
}
if($verified.Build.OfflineDebugRemoval) {
Copy-IndependentBoundFile $verified.Build.OfflineDebugRemoval.Path 'Validation/OfflineDebugRemoval.json' $verified.Build.OfflineDebugRemoval.Sha256
}
foreach($file in Get-ChildItem -LiteralPath (Split-Path -Parent $verified.BootPath) -File) {
if($file.Extension -notin @('.png','.log','.txt') -or ($file.Attributes -band [IO.FileAttributes]::ReparsePoint)) {continue}
Copy-IndependentBoundFile $file.FullName ('Validation/Guest/'+$file.Name) (Get-IndependentHash $file.FullName)
}
[ordered]@{Kind='XfeIndependentFirmwareLock';Firmware=$verified.FirmwareLock.Firmware} | ConvertTo-Json -Depth 4 |
Set-Content -LiteralPath (Join-Path $package 'Validation/Firmware.json') -Encoding UTF8
@'
[CmdletBinding()]
param([string]$QemuPath,[ValidateSet('gtk','sdl')][string]$DisplayBackend='gtk',[ValidateSet('auto','whpx','tcg')][string]$Acceleration='auto',[string]$MonitorResolution,[ValidateRange(1,8)][int]$DisplayCount=1,[switch]$Fullscreen,[switch]$FitToWindow,[switch]$NativePixels,[switch]$TraceConsoleRefresh,[switch]$PlanOnly)
$ErrorActionPreference='Stop'
& (Join-Path $PSScriptRoot 'Tools/IndependentKernel/Open.ps1') -Edition '__EDITION__' -ImagePath (Join-Path $PSScriptRoot 'System.img') -VerifiedBootResult (Join-Path $PSScriptRoot 'Validation/Boot.json') -QemuPath $QemuPath -DisplayBackend $DisplayBackend -Acceleration $Acceleration -MonitorResolution $MonitorResolution -DisplayCount $DisplayCount -Fullscreen:$Fullscreen -FitToWindow:$FitToWindow -NativePixels:$NativePixels -TraceConsoleRefresh:$TraceConsoleRefresh -PlanOnly:$PlanOnly
'@.Replace('__EDITION__',$Edition) | Set-Content -LiteralPath (Join-Path $package 'Start.ps1') -Encoding UTF8
@'
# XFESystem 独立内核 RAM 实验版
此包使用自研 C# 内核/HAL/调度与 Microsoft .NET 10 NativeAOT 运行时,不包含 Cosmos 内核或运行时。完整第三方声明位于 Licenses;自研范围和外部引用见 ThirdParty.md。
文件保存在内存中,退出或关机后消失。当前执行一个 BSP,CPU 软件绘制,x87/SSE 上下文;没有持久化存储或独立进程隔离。
Validation/Boot.json 记录本包同一镜像的真实 QEMU 桌面、PS/2 输入和浏览器 DOM 验收;BrowserAcceptancePassed 字段单独说明 Monaco 验收结果。Validation/Host.json 与逐项 TRX/原生测试记录保留宿主结果,宿主通过不能替代来宾执行。
Validation/Boot.json 与 Validation/Guest/BingEvidence.json(仅通过时携带)记录独立 QEMU 来宾的原生 Bing HTTPS 验收;包清单 BingNativeHttpsPassed/NetworkAcceptancePassed 只绑定这份真实证据,不由网卡启用状态推断。其范围仅 QEMU e1000 82540EM、显式静态 NAT 地址、验证证书的 Bing HTML/DOM 和实际像素呈现;不表示完整 CSS/JavaScript 或 Windows 浏览器像素一致。FullCssParityVerified 仍为 false。
这是 RAM 实验版。实体启动、实体 USB HID 输入、实体网络/DHCP、CUDA/GPU 和完整稳定性均未验收,不能标为完整发行版。CMOS RTC 按 UTC 解释;实体机的 RTC 配置和准确性尚未验收。
在 Windows 安装 QEMU 后执行:
powershell -NoProfile -ExecutionPolicy Bypass -File .\Start.ps1 -QemuPath "C:\Program Files\qemu\qemu-system-x86_64.exe"
默认打开 TCG / Q35 / x86_64 / 1 GiB 的 GTK 交互窗口,使用 Q35 内置 PS/2 键盘鼠标,RTC 设为 UTC。网络参数以已绑定的 NativeNetworkProfile 为准;启用时使用 QEMU user NAT/e1000 与显式静态地址,不使用宿主代理。可选择 -DisplayBackend sdl 或 -Fullscreen。启动前核验完整包、镜像、ELF、固件、验收记录及所有编译源码快照;每次复制镜像和固件变量到新的 build/IndependentKernel/Interactive-* 会话目录,串口日志位于同目录 Serial.log。关闭 QEMU 窗口结束。
源码与资源快照在 SourceSnapshot,宿主测试源码快照在 HostSourceSnapshot,所有文件 SHA256 在 Manifest.json。它们保存本次实际构建和测试的字节;完整复现命令与版本锁要求见 Reproduce.md。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
@'
# 复现与校验
在 XFESystem 仓库中使用 Manifest.json 的源码哈希核对 SourceSnapshot 中所有文件;保留仓库目录结构,按各 packages.lock.json 恢复固定依赖,工具版本锁位于 Tools/IndependentKernel/Runtime/NativeToolchain.json 与 Tools/CSharpBoot/BflatToolchain.json。
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1
python Tools/IndependentKernel/TestBoot.py <新生成的Kernel.elf> --virtual-cpus 8 --desktop --browser --documents --bing --timeout 360
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/TestCSharp.ps1
python Tools/IndependentKernel/CollectLicenses.py
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Package.ps1 -ImagePath <通过的System.img> -VerifiedBootResult <通过的Result.json> -VerifiedHostResult <宿主Result.json>
编译、链接成功不等于运行验收;每次新建产物需要新 guest/host 记录。本包原始精确编译命令保存在 Validation/BuildReceipt.json;完整包与携带的源码快照、固件可通过下列命令只读核验:
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Open.ps1 -ImagePath System.img -VerifiedBootResult Validation/Boot.json -ValidateOnly
SourceSnapshot 只保存实际编译输入,复现需要完整仓库中匹配的工具与锁文件。HostSourceSnapshot 保存实际宿主检查输入。各原始 JSON 的旧绝对路径用于出处记录;打包启动入口通过包内固定相对路径定位已验证文件。
'@ | Set-Content -LiteralPath (Join-Path $package 'Reproduce.md') -Encoding UTF8
if($Edition -eq 'Console') {
@'
# XFESystem Console 独立内核 RAM 实验版
这是独立构建的纯控制台系统,保留以前 Rust 控制台的黑底、彩色启动标题、文本提示符和键盘操作方式。系统内核仍为自研 C# 内核,运行时为 Microsoft .NET 10 NativeAOT;不启动图形桌面、窗口、浏览器或文档编辑器。
输入 help 查看命令。支持目录、RAM 文件读写、命令历史和光标编辑,也可运行内置 ConsoleIL 与 XFEConsole 示例。网络关闭。文件只保存在本次 RAM 会话,关机后消失。
在 Windows 安装 QEMU 后执行:
powershell -NoProfile -ExecutionPolicy Bypass -File .\Start.ps1 -QemuPath "C:\Program Files\qemu\qemu-system-x86_64.exe"
默认打开 TCG / Q35 / x86_64 / 1 GiB 的 GTK 控制台窗口,使用 Q35 内置 PS/2 键盘。可选择 -DisplayBackend sdl 或 -Fullscreen。启动前核验镜像、内核、固件、实际来宾命令执行证据和全部源码快照。每次启动使用独立镜像副本,串口日志在 build/IndependentKernel/Interactive-Console-* 下。
Validation/Boot.json 记录本包同一镜像的真实 QEMU 键盘输入、RAM 文件读写、命令历史、光标编辑与帧缓冲证据;Validation/Host.json 和逐项 TRX/原生记录保存宿主检查结果。第三方许可位于 Licenses,自研范围见 ThirdParty.md。源码及资源快照位于 SourceSnapshot,宿主检查源码位于 HostSourceSnapshot,文件 SHA256 位于 Manifest.json。
这是 RAM 实验版,实体启动与输入、持久存储、SMP、GPU/CUDA 和长期稳定性仍未验收,FullReleaseQualified=false。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
@'
# 复现与校验
从完整 XFESystem 仓库按锁文件恢复依赖,并核对包内 SourceSnapshot 与 Manifest.json 的源码哈希。
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1 -Edition Console
python Tools/IndependentKernel/TestBoot.py <新生成的Kernel.elf> --virtual-cpus 8 --console --timeout 360
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/TestCSharp.ps1
python Tools/IndependentKernel/CollectLicenses.py
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Package.ps1 -Edition Console -ImagePath <通过的System.img> -VerifiedBootResult <通过的Result.json> -VerifiedHostResult <宿主Result.json>
每次新建产物都需要新的 guest/host 记录。包内只读核验:
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Open.ps1 -Edition Console -ImagePath System.img -VerifiedBootResult Validation/Boot.json -ValidateOnly
Validation/BuildReceipt.json 保存精确编译命令。SourceSnapshot 只保存实际编译输入,复现仍需要完整仓库及匹配的工具和锁文件。JSON 内旧绝对路径用于出处记录,包内启动入口使用固定相对路径。
'@ | Set-Content -LiteralPath (Join-Path $package 'Reproduce.md') -Encoding UTF8
}
Assert-IndependentSourceMap $verified.Snapshot $taskRoot 'final guest source snapshot'
Assert-IndependentSourceMap $hostProof.SourceHashes $taskRoot 'final host source snapshot'
$files=@(foreach($file in Get-ChildItem -LiteralPath $package -File -Recurse | Sort-Object FullName) {
[ordered]@{Path=$file.FullName.Substring($package.Length+1).Replace('\','/');Bytes=$file.Length;Sha256=(Get-IndependentHash $file.FullName)}
})
if($verified.ImageManifest.UsbWorkspace -eq $true) {
@'
# XFESystem USB 工作区实验版
此包只包含所选的一个系统。启动 U 盘可用时,X: 的文件和外观设置提交到 Workspace.bin 的双数据区,UsbFiles 对应启动 FAT32 卷的 UserFiles 文件夹。
实体镜像使用自动显示模式。Desktop 的虚拟显示器默认 3840x2160,可用 -MonitorResolution 1280x800 改为较小的测试显示器;该参数不改写镜像。-Acceleration auto 在宿主已运行 Hypervisor 时选择 WHPX,否则选择 TCG,亦可显式指定 -Acceleration tcg 或 whpx;不会开启 Windows 系统功能。启动计划只记录请求参数,不能证明硬件加速或实体 240 Hz 已生效。Windows 流畅度共同标准见 DesktopFluidityStandard.md;实体扫描频率、VSync、多核执行和 Windows 流畅度一致尚未通过验收。
run X:\UsbFiles\UsbHello.dll 可运行 U 盘普通 FAT 文件中的标准 .NET 控制台示例,仍受 ConsoleIL 指令与 API 范围限制。Workspace.bin 为 64 MiB 容器,两个数据区交替提交;现有每文件及工作区配额继续生效。Temporary 和 System/Diagnostics 是临时数据。
没有唯一匹配启动分区的可用 USB BOT 设备时,明确回退到 RAM,会话文件不保留。验证范围为 QEMU xHCI/USB BOT,实体 UAS、实体启动输入和掉电行为仍需实测;这不是完整发行版。第三方来源与许可见 Licenses。
Start.ps1 使用独立的虚拟 USB 工作盘;系统原始镜像和验证记录保留。Validation/UsbStorage/Result.json 记录两次真实冷启动、文件和设置回读、FAT 程序加载与实际 framebuffer 证据。
其他磁盘位于 X:\Disks,图形版文件管理器的 Disks 按钮可打开。原生 NVMe、AHCI SATA 和其他 USB BOT 磁盘支持 GPT / MBR(含逻辑分区),普通 NTFS、FAT、exFAT 数据分区可创建、读写、修改、重命名和删除文件。终端可 cd /Disks 后 ls,进入对应分区执行文件命令;run 可读取其中兼容 ConsoleIL 的标准 .NET 控制台程序集。当前单文件读写上限 4 MiB。启动时发现并挂载磁盘,其他磁盘的在线热插拔重挂载尚未实现。
EFI / 恢复等保护分区只读;脏卷或休眠 Windows 卷只读,BitLocker 不解密。每次修改设置脏标记并同步缓存,失败后保留标记;没有实现 Windows $LogFile 回放或掉电原子事务。不会格式化、分区、TRIM 或安装到其他磁盘。实体控制器、真实硬盘持久化及掉电恢复仍未验收。文件系统使用明确声明的 DiscUtils 外部库;驱动与分区边界由本项目实现。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
if($verified.MultiDisplay) {
@'
显示器管理:打开 Settings > Display(或 Win+P),选择屏幕编号,切换 Duplicate / Extend / Show only the main display。Extend 下拖动编号块调整排列,选中屏幕后可设为主屏;Apply 后必须在 15 秒内 Keep changes,否则恢复原布局。Identify 在每个实际输出上显示各自编号。确认后的布局保存到启动 U 盘,硬件拓扑变化时使用安全默认布局。
虚拟机双屏启动:Start.ps1 -DisplayCount 2 -QemuPath <已安装的QEMU路径>。实体屏幕需在启动前连接,当前只管理固件交付的独立帧缓冲;分辨率和全局缩放沿用启动模式,没有原生 GPU 端口热插拔、运行中硬件模式切换或逐屏 DPI。两块独立虚拟显卡的三次冷启动和实际双输出验收位于 Validation/MultiDisplay;不能等同实体显卡全部接口可用。
'@ | Add-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
}
$files=@(foreach($file in Get-ChildItem -LiteralPath $package -File -Recurse | Sort-Object FullName) {
[ordered]@{Path=$file.FullName.Substring($package.Length+1).Replace('\','/');Bytes=$file.Length;Sha256=(Get-IndependentHash $file.FullName)}
})
}
$manifest=[ordered]@{Kind=$(if($storageLabel -ceq 'USB'){if($Edition -eq 'Console'){'XfeIndependentUsbConsolePackage'}else{'XfeIndependentUsbDesktopPackage'}}else{if($Edition -eq 'Console'){'XfeIndependentRamConsolePackage'}else{'XfeIndependentRamDesktopPackage'}});Edition=$Edition;Version=$Version;ContainsCosmos=$false;
ResolutionPolicy=$(if($verified.ImageManifest.Resolution -ceq '0x0'){'AutomaticFirmwareMonitorMode'}else{'FixedVirtualTestMode'});WindowsFluidityParityPassed=$false;Physical240HzVerified=$false;
RamOnly=[bool]$verified.Boot.RamOnly;StorageEnabled=[bool]$verified.Boot.StorageEnabled;FullReleaseQualified=$false;PhysicalHardwareAcceptancePassed=$false;
UsbStorageAcceptancePassed=[bool]$verified.Boot.UsbStorageAcceptancePassed;
MultiDisplayAcceptancePassed=[bool]$verified.Boot.MultiDisplayAcceptancePassed;DisplayHotplugAcceptancePassed=$false;
OtherDiskAccessEnabled=[bool]$verified.Build.Configuration.OtherDiskAccessEnabled;OtherDiskAcceptancePassed=[bool]$verified.Boot.OtherDiskAcceptancePassed;
UsbHidAcceptancePassed=$false;NetworkAcceptancePassed=($verified.BingNativeHttpsPassed -eq $true);GpuAcceptancePassed=$false;
BingNativeHttpsPassed=($verified.BingNativeHttpsPassed -eq $true);FullCssParityVerified=$false;
NetworkAcceptanceScope='Only image-bound QEMU e1000/static user NAT and validated native HTTPS Bing HTML/DOM/presentation when BingNativeHttpsPassed is true; physical NIC/DHCP/general web compatibility are unverified';
NativeNetworkProfile=$verified.Boot.NativeNetworkProfile;
GuestAcceptancePassed=$true;HostAcceptancePassed=$true;BrowserAcceptancePassed=[bool]$verified.Boot.BrowserAcceptancePassed;
NoRenderFailures=[bool]$verified.NoRenderFailures;MonacoRasterPaintCompleted=[bool]$verified.MonacoRasterPaintCompleted;
DocumentWorkflowPassed=[bool]$verified.DocumentWorkflowPassed;
TerminalWorkflowPassed=[bool]$verified.TerminalWorkflowPassed;ConsoleAcceptancePassed=[bool]$verified.ConsoleAcceptancePassed;
MonacoKeyboardEditedModel=[bool]$verified.MonacoKeyboardEditedModel;
ImageSha256=$verified.Boot.ImageSha256;KernelSha256=$verified.Boot.KernelSha256;
SourceSnapshotSha256=$verified.Boot.SourceSnapshotSha256;BootReceiptSha256=(Get-IndependentHash $verified.BootPath);
HostReceiptSha256=(Get-IndependentHash $hostPath);LicenseManifestSha256=(Get-IndependentHash $licensesPath);
Files=$files}
$manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $package 'Manifest.json') -Encoding UTF8
# Exercise the relocated package's own verifier before creating the ZIP.
$null=& (Join-Path $package 'Tools/IndependentKernel/Open.ps1') -Edition $Edition -ImagePath (Join-Path $package 'System.img') -VerifiedBootResult (Join-Path $package 'Validation/Boot.json') -ValidateOnly
Compress-Archive -Path (Join-Path $package '*') -DestinationPath $zipPath -CompressionLevel Optimal
Add-Type -AssemblyName System.IO.Compression.FileSystem
$expectedEntries=[Collections.Generic.Dictionary[string,object]]::new([StringComparer]::Ordinal)
foreach($file in $files){$expectedEntries.Add([string]$file.Path,$file)}
$expectedEntries.Add('Manifest.json',[pscustomobject]@{Bytes=(Get-Item -LiteralPath (Join-Path $package 'Manifest.json')).Length;
Sha256=(Get-IndependentHash (Join-Path $package 'Manifest.json'))})
$zip= [IO.Compression.ZipFile]::OpenRead($zipPath)
try {
foreach($entry in $zip.Entries) {
$name=$entry.FullName.Replace('\','/')
if($name.EndsWith('/')){continue}
if(!$expectedEntries.ContainsKey($name)){throw ('Unexpected or duplicate ZIP entry: '+$name)}
$expected=$expectedEntries[$name]
if($entry.Length -ne $expected.Bytes){throw ('ZIP entry length changed: '+$name)}
$stream=$entry.Open();$sha=[Security.Cryptography.SHA256]::Create()
try {$actual=[BitConverter]::ToString($sha.ComputeHash($stream)).Replace('-','').ToLowerInvariant()}
finally {$stream.Dispose();$sha.Dispose()}
if($actual -cne $expected.Sha256){throw ('ZIP readback SHA256 differs: '+$name)}
[void]$expectedEntries.Remove($name)
}
if($expectedEntries.Count){throw ('ZIP is missing files: '+($expectedEntries.Keys -join ', '))}
} finally {$zip.Dispose()}
$receipt=[ordered]@{Kind='XfeIndependentKernelPackageReceipt';Edition=$Edition;Passed=$true;PackageDirectory=$package;ZipPath=$zipPath;
ZipSha256=(Get-IndependentHash $zipPath);ManifestSha256=(Get-IndependentHash (Join-Path $package 'Manifest.json'));
ImageSha256=$verified.Boot.ImageSha256;KernelSha256=$verified.Boot.KernelSha256;GuestAcceptancePassed=$true;
HostAcceptancePassed=$true;RelocatedPackageVerificationPassed=$true;ZipReadbackPassed=$true;
NoRenderFailures=[bool]$verified.NoRenderFailures;MonacoRasterPaintCompleted=[bool]$verified.MonacoRasterPaintCompleted;
DocumentWorkflowPassed=[bool]$verified.DocumentWorkflowPassed;
TerminalWorkflowPassed=[bool]$verified.TerminalWorkflowPassed;ConsoleAcceptancePassed=[bool]$verified.ConsoleAcceptancePassed;
MonacoKeyboardEditedModel=[bool]$verified.MonacoKeyboardEditedModel;
NetworkAcceptancePassed=($verified.BingNativeHttpsPassed -eq $true);BingNativeHttpsPassed=($verified.BingNativeHttpsPassed -eq $true);
FullCssParityVerified=$false;
FullReleaseQualified=$false;FinishedUtc=[DateTime]::UtcNow.ToString('o')}
$receipt | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath ($package+'.json') -Encoding UTF8
Write-Host ('Independent RAM experiment package: '+$zipPath)
return [pscustomobject]$receipt
} catch {
[ordered]@{Kind='XfeIndependentKernelPackageReceipt';Edition=$Edition;Passed=$false;PackageDirectory=$package;Error=$_.Exception.Message} |
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath ($package+'.failure.json') -Encoding UTF8
throw
}
[CmdletBinding()]
param(
[ValidateSet('Desktop','Console')][string]$Edition='Desktop',
[Parameter(Mandatory=$true)][string]$ImagePath,
[Parameter(Mandatory=$true)][string]$VerifiedBootResult,
[Parameter(Mandatory=$true)][string]$VerifiedHostResult,
[string]$ToolsRoot,
[string]$Version='dev',
[string]$OutputDirectory
)
$ErrorActionPreference='Stop'
$Edition=if($Edition -eq 'Console'){'Console'}else{'Desktop'}
$taskRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if($Version -cnotmatch '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'){throw 'Version must be a short filename-safe label.'}
# The launcher owns the same mandatory image, kernel, source and desktop gates.
$verified=. (Join-Path $PSScriptRoot 'Open.ps1') -Edition $Edition -ImagePath $ImagePath -VerifiedBootResult $VerifiedBootResult -ToolsRoot $ToolsRoot -ValidateOnly
if($verified.Packaged){throw 'Package a verified repository build, rather than repackaging an existing distribution.'}
if($verified.ImageManifest.Edition -ceq 'Dual'){throw 'Edition packages require their separately verified single-edition images.'}
if($verified.ImageManifest.UsbWorkspace -eq $true -and $verified.ImageManifest.Resolution -cne '0x0') {
throw 'USB packages require automatic monitor resolution; a fixed QEMU test resolution is not a deliverable display policy.'
}
if($verified.ImageManifest.DeploymentPolicy -cne 'SingleEditionOnly' -or $verified.Boot.SingleEditionImageProof.Passed -ne $true) {
throw 'New packages require actual single-system image inspection and a boot proof with no edition selection menu.'
}
# Validation-only dot-sourcing shares validators without opening a guest.
$hostPath=(Resolve-Path -LiteralPath $VerifiedHostResult).Path
$hostProof=Read-IndependentJson $hostPath
if($hostProof.HostOnly -ne $true -or $hostProof.GuestExecution -ne $false -or $hostProof.Passed -ne $true -or !$hostProof.SourceHashes -or !$hostProof.Suites) {
throw 'A passing source-bound host test receipt is required.'
}
Assert-IndependentSourceMap $hostProof.SourceHashes $taskRoot 'host verification'
if(!$hostProof.SourceHashes.PSObject.Properties['Source/XFESystem.NativeKernel/Ps2Decoder.cs'] -or
!$hostProof.SourceHashes.PSObject.Properties['Tests/XFESystem.Platform.Tests/XFESystem.Platform.Tests.csproj']) {
throw 'Host verification must include the independent Platform tests and the production PS/2 decoder.'
}
foreach($entry in $verified.Snapshot.PSObject.Properties) {
$hostEntry=$hostProof.SourceHashes.PSObject.Properties[$entry.Name]
if($hostEntry -and [string]$hostEntry.Value -cne [string]$entry.Value) { throw ('Host and guest source hashes differ: '+$entry.Name) }
}
$hostArtifacts=@()
$suiteNames=@()
foreach($suite in $hostProof.Suites) {
if([string]$suite.Suite -cnotmatch '^[A-Za-z0-9._-]+$' -or $suiteNames -contains $suite.Suite -or $suite.Failed -ne 0 -or [int]$suite.Passed -le 0) {
throw 'Host suite names and successful counters are required.'
}
$suiteNames+=([string]$suite.Suite)
$logPath=(Resolve-Path -LiteralPath $suite.Log).Path
if($suite.LogSha256){Assert-IndependentHash $logPath $suite.LogSha256 ('host log '+$suite.Suite)}
$hostArtifacts+= [pscustomobject]@{Source=$logPath;Destination=('Validation/Host/'+$suite.Suite+'.log');Sha256=(Get-IndependentHash $logPath)}
if($suite.Trx) {
$trxPath=(Resolve-Path -LiteralPath $suite.Trx).Path
if($suite.TrxSha256){Assert-IndependentHash $trxPath $suite.TrxSha256 ('host TRX '+$suite.Suite)}
[xml]$trx=Get-Content -LiteralPath $trxPath -Raw -Encoding UTF8
$counter=$trx.SelectSingleNode("//*[local-name()='Counters']")
if(!$counter -or [int]$counter.failed -ne 0 -or [int]$counter.passed -ne [int]$suite.Passed -or
[int]$counter.executed -ne [int]$counter.passed -or [int]$counter.total -ne [int]$counter.passed) {
throw ('Actual host TRX counters are incomplete or differ: '+$suite.Suite)
}
$hostArtifacts+= [pscustomobject]@{Source=$trxPath;Destination=('Validation/Host/'+$suite.Suite+'.trx');Sha256=(Get-IndependentHash $trxPath)}
} elseif($suite.Receipt -and $suite.ReceiptSha256) {
$nativePath=(Resolve-Path -LiteralPath $suite.Receipt).Path
Assert-IndependentHash $nativePath $suite.ReceiptSha256 ('native host receipt '+$suite.Suite)
$native=Read-IndependentJson $nativePath
if($native.Passed -ne $true -or $native.Failed -ne 0 -or $null -eq $native.ExitCode -or $native.ExitCode -ne 0 -or
[int]$native.PassedCount -ne [int]$suite.Passed) { throw ('Native host test execution did not pass: '+$suite.Suite) }
$hostArtifacts+= [pscustomobject]@{Source=$nativePath;Destination=('Validation/Host/'+$suite.Suite+'.json');Sha256=(Get-IndependentHash $nativePath)}
} else {throw ('Actual TRX or source-bound native execution receipt is required: '+$suite.Suite)}
}
if($suiteNames -notcontains 'Platform' -or $suiteNames -notcontains 'NativeContracts' -or $suiteNames -notcontains 'Network' -or $suiteNames -notcontains 'NativeTls' -or $suiteNames -notcontains 'ConsoleEdition') {
throw 'The final host receipt must include Platform, NativeContracts, Network, NativeTls and ConsoleEdition suites. Network/NativeTls host acceptance does not prove guest HTTPS acceptance.'
}
if($verified.Build.Configuration.OtherDiskAccessEnabled -eq $true -and $suiteNames -notcontains 'Storage') {throw 'Other-disk delivery requires actual Storage filesystem host tests.'}
if($verified.Build.Configuration.MultiprocessorBackend -ceq 'LimineApKernelJobsV1' -and $suiteNames -notcontains 'MultiprocessorEvidenceGuards') {
throw 'Native AP kernel delivery requires the source-bound CPU evidence guard suite.'
}
$licensesRoot=Join-Path $PSScriptRoot 'Licenses'
$licensesPath=Get-IndependentFile $licensesRoot 'Manifest.json'
$licenses=Read-IndependentJson $licensesPath
if($licenses.Schema -cne 'XFESystem.IndependentLicenses.V1' -or $licenses.ContainsCosmos -ne $false -or
$licenses.ExcludesLegacyCollectedGraph -ne $true -or !$licenses.Files -or [int]$licenses.PackageCount -ne @($licenses.Packages).Count) {
throw 'The complete independent application license manifest is required.'
}
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Source/XFESystem.NativeRuntime/Application/packages.lock.json') $licenses.ApplicationLockSha256 'license application lock'
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Tools/IndependentKernel/Runtime/NativeToolchain.json') $licenses.RuntimeToolchainLockSha256 'license runtime toolchain lock'
Assert-IndependentHash (Get-IndependentFile $taskRoot 'Tools/JintCleanupPatch/packages.lock.json') $licenses.JintPatchToolLockSha256 'license Jint patch tool lock'
Assert-IndependentHash (Get-IndependentFile $licensesRoot 'LicenseSources.json') $licenses.SourcesSha256 'license sources manifest'
foreach($binding in @(
@('Tools/TlsEntropyPatch/Packages.lock.json','TlsEntropyPatchToolLockSha256'),
@('Source/XFESystem.Tls/DependencyManifest.json','TlsDependencyManifestSha256'),
@('Source/XFESystem.Tls/Packages.lock.json','TlsProjectLockSha256'),
@('Source/XFESystem.NativeRuntime/Application/Security/Manifest.json','TrustedRootManifestSha256'),
@('Source/XFESystem.NativeRuntime/Application/Security/MozillaRoots.pem','TrustedRootSnapshotSha256')
)) {
$expected=$licenses.PSObject.Properties[$binding[1]]
if(!$expected -or [string]$expected.Value -cnotmatch '^[a-f0-9]{64}$') {throw ('Missing TLS/root license source binding: '+$binding[1])}
Assert-IndependentHash (Get-IndependentFile $taskRoot $binding[0]) ([string]$expected.Value) ('license '+$binding[0])
}
if($licenses.GuestAcceptancePassed -ne $false -or $licenses.NativeTlsAcceptancePassed -ne $false -or $licenses.FullReleaseQualified -ne $false) {
throw 'License collection cannot assert guest TLS acceptance or full release qualification.'
}
$tlsLicense=@($licenses.Packages | Where-Object {$_.Id -ceq 'BouncyCastle.Cryptography'})
if($tlsLicense.Count -ne 1 -or $tlsLicense[0].Version -cne '2.7.0' -or !$tlsLicense[0].LicenseFiles -or
$tlsLicense[0].DependencyManifestSha256 -cne $licenses.TlsDependencyManifestSha256) {
throw 'The pinned BouncyCastle package and full license are required.'
}
$rootLicenseNames=@('Security/MozillaRoots-20260925.pem','Security/MozillaRoots-Manifest.json','Security/MozillaRoots-MPL-2.0.txt')
foreach($required in $rootLicenseNames) {
if(@($licenses.Files | Where-Object {$_.Path -ceq $required}).Count -ne 1) {throw ('Missing trusted root source/license: '+$required)}
}
foreach($binding in @(
@('Security/MozillaRoots-20260925.pem','TrustedRootSnapshotSha256'),
@('Security/MozillaRoots-Manifest.json','TrustedRootManifestSha256')
)) {
$entry=@($licenses.Files | Where-Object {$_.Path -ceq $binding[0]})[0]
if($entry.Sha256 -cne $licenses.PSObject.Properties[$binding[1]].Value) {throw ('Collected trusted root source differs: '+$binding[0])}
}
$licenseNames=@()
foreach($entry in $licenses.Files) {
if($licenseNames -contains $entry.Path) {throw ('Duplicate license path: '+$entry.Path)}
$licenseNames+=([string]$entry.Path)
$path=Get-IndependentFile $licensesRoot $entry.Path
Assert-IndependentHash $path $entry.Sha256 ('license '+$entry.Path)
if((Get-Item -LiteralPath $path).Length -ne $entry.Bytes) {throw ('License length differs: '+$entry.Path)}
}
if(!$OutputDirectory){$OutputDirectory=Join-Path $taskRoot 'dist'}
$outputRoot=[IO.Path]::GetFullPath($OutputDirectory)
[void][IO.Directory]::CreateDirectory($outputRoot)
$storageLabel=if($verified.ImageManifest.UsbWorkspace -eq $true){'USB'}else{'RAM'}
$packageId='XFESystem-'+$Edition+'-Independent-'+$storageLabel+'-'+$Version+'-'+[DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()+'-'+[Guid]::NewGuid().ToString('N').Substring(0,8)
$package=Join-Path $outputRoot $packageId
$zipPath=$package+'.zip'
if((Test-Path -LiteralPath $package) -or (Test-Path -LiteralPath $zipPath)){throw 'Refusing to overwrite an existing package or ZIP.'}
[void][IO.Directory]::CreateDirectory($package)
function Copy-IndependentBoundFile([string]$Source,[string]$Relative,[string]$Expected) {
Assert-IndependentHash $Source $Expected ('package input '+$Relative)
if([IO.Path]::IsPathRooted($Relative)){throw 'Package destination must be relative.'}
$destination=[IO.Path]::GetFullPath((Join-Path $package $Relative))
if(!$destination.StartsWith($package+[IO.Path]::DirectorySeparatorChar,[StringComparison]::OrdinalIgnoreCase)) {throw 'Package destination escaped the output directory.'}
[void][IO.Directory]::CreateDirectory((Split-Path -Parent $destination))
Copy-Item -LiteralPath $Source -Destination $destination
Assert-IndependentHash $destination $Expected ('package copy '+$Relative)
}
try {
Copy-IndependentBoundFile $verified.ImagePath 'System.img' $verified.Boot.ImageSha256
Copy-IndependentBoundFile $verified.ImageManifestPath 'System.img.json' (Get-IndependentHash $verified.ImageManifestPath)
Copy-IndependentBoundFile $verified.KernelPath 'Kernel.elf' $verified.Boot.KernelSha256
Copy-IndependentBoundFile $verified.BootPath 'Validation/Boot.json' (Get-IndependentHash $verified.BootPath)
Copy-IndependentBoundFile $verified.BuildPath 'Validation/BuildReceipt.json' $verified.Boot.BuildReceiptSha256
Copy-IndependentBoundFile $verified.SnapshotPath 'Validation/SourceSnapshot.json' $verified.Boot.SourceSnapshotSha256
Copy-IndependentBoundFile $hostPath 'Validation/Host.json' (Get-IndependentHash $hostPath)
foreach($entry in $verified.Snapshot.PSObject.Properties) {
Copy-IndependentBoundFile (Get-IndependentFile $taskRoot $entry.Name) ('SourceSnapshot/'+$entry.Name) ([string]$entry.Value)
}
foreach($entry in $hostProof.SourceHashes.PSObject.Properties) {
Copy-IndependentBoundFile (Get-IndependentFile $taskRoot $entry.Name) ('HostSourceSnapshot/'+$entry.Name) ([string]$entry.Value)
}
foreach($artifact in $hostArtifacts) {Copy-IndependentBoundFile $artifact.Source $artifact.Destination $artifact.Sha256}
foreach($file in Get-ChildItem -LiteralPath $licensesRoot -File -Recurse) {
$relative=$file.FullName.Substring($licensesRoot.Length+1).Replace('\','/')
Copy-IndependentBoundFile $file.FullName ('Licenses/'+$relative) (Get-IndependentHash $file.FullName)
}
foreach($firmware in @($verified.FirmwareCode,$verified.FirmwareVars)) {
Copy-IndependentBoundFile $firmware ('Firmware/'+[IO.Path]::GetFileName($firmware)) (Get-IndependentHash $firmware)
}
[void][IO.Directory]::CreateDirectory((Join-Path $package 'Tools/IndependentKernel'))
Copy-IndependentBoundFile (Join-Path $PSScriptRoot 'Open.ps1') 'Tools/IndependentKernel/Open.ps1' (Get-IndependentHash (Join-Path $PSScriptRoot 'Open.ps1'))
Copy-IndependentBoundFile (Join-Path $PSScriptRoot 'VerifyMultiDisplay.ps1') 'Tools/IndependentKernel/VerifyMultiDisplay.ps1' (Get-IndependentHash (Join-Path $PSScriptRoot 'VerifyMultiDisplay.ps1'))
Copy-IndependentBoundFile $PSCommandPath 'Tools/IndependentKernel/Package.ps1' (Get-IndependentHash $PSCommandPath)
Copy-IndependentBoundFile (Join-Path $taskRoot 'THIRD_PARTY.md') 'ThirdParty.md' (Get-IndependentHash (Join-Path $taskRoot 'THIRD_PARTY.md'))
Copy-IndependentBoundFile (Join-Path $taskRoot 'docs/DesktopFluidityStandard.md') 'DesktopFluidityStandard.md' (Get-IndependentHash (Join-Path $taskRoot 'docs/DesktopFluidityStandard.md'))
Copy-IndependentBoundFile (Join-Path $taskRoot 'Tools/DesktopFluidity/Standard.json') 'Validation/DesktopFluidityStandard.json' (Get-IndependentHash (Join-Path $taskRoot 'Tools/DesktopFluidity/Standard.json'))
if($verified.BingNativeHttpsPassed -eq $true) {
Copy-IndependentBoundFile $verified.BingEvidencePath 'Validation/Guest/BingEvidence.json' $verified.BingEvidenceSha256
}
if($verified.Boot.GpuDiscoveryEvidence) {
Copy-IndependentBoundFile (Get-IndependentFile (Split-Path -Parent $verified.BootPath) 'GpuDiscoveryEvidence.json') 'Validation/Guest/GpuDiscoveryEvidence.json' $verified.Boot.GpuDiscoveryEvidence.Sha256
}
if($verified.Boot.MultiprocessorEvidence) {
Copy-IndependentBoundFile $verified.Boot.MultiprocessorEvidence.Path 'Validation/MultiprocessorEvidence.json' $verified.Boot.MultiprocessorEvidence.Sha256
}
if($verified.UsbStorageProofPath) {
Copy-IndependentBoundFile $verified.UsbStorageProofPath 'Validation/UsbStorage/Result.json' $verified.Boot.UsbStorageProof.Sha256
foreach($file in $verified.UsbStorageProof.EvidenceFiles) {
Copy-IndependentBoundFile (Get-IndependentFile $verified.UsbStorageProofRoot $file.Path) ('Validation/UsbStorage/'+$file.Path) $file.Sha256
}
}
if($verified.MultiDisplay) {
Copy-IndependentBoundFile $verified.MultiDisplay.Path 'Validation/MultiDisplay/Result.json' $verified.Boot.MultiDisplayProof.Sha256
foreach($file in $verified.MultiDisplay.Proof.EvidenceFiles) {
Copy-IndependentBoundFile (Get-IndependentFile $verified.MultiDisplay.Root $file.Path) ('Validation/MultiDisplay/'+$file.Path) $file.Sha256
}
}
if($verified.Build.OfflineDebugRemoval) {
Copy-IndependentBoundFile $verified.Build.OfflineDebugRemoval.Path 'Validation/OfflineDebugRemoval.json' $verified.Build.OfflineDebugRemoval.Sha256
}
foreach($file in Get-ChildItem -LiteralPath (Split-Path -Parent $verified.BootPath) -File) {
if($file.Extension -notin @('.png','.log','.txt') -or ($file.Attributes -band [IO.FileAttributes]::ReparsePoint)) {continue}
Copy-IndependentBoundFile $file.FullName ('Validation/Guest/'+$file.Name) (Get-IndependentHash $file.FullName)
}
[ordered]@{Kind='XfeIndependentFirmwareLock';Firmware=$verified.FirmwareLock.Firmware} | ConvertTo-Json -Depth 4 |
Set-Content -LiteralPath (Join-Path $package 'Validation/Firmware.json') -Encoding UTF8
@'
[CmdletBinding()]
param([string]$QemuPath,[ValidateSet('gtk','sdl')][string]$DisplayBackend='gtk',[ValidateSet('auto','whpx','tcg')][string]$Acceleration='auto',[string]$MonitorResolution,[ValidateRange(1,8)][int]$DisplayCount=1,[switch]$Fullscreen,[switch]$FitToWindow,[switch]$NativePixels,[switch]$TraceConsoleRefresh,[switch]$PlanOnly)
$ErrorActionPreference='Stop'
& (Join-Path $PSScriptRoot 'Tools/IndependentKernel/Open.ps1') -Edition '__EDITION__' -ImagePath (Join-Path $PSScriptRoot 'System.img') -VerifiedBootResult (Join-Path $PSScriptRoot 'Validation/Boot.json') -QemuPath $QemuPath -DisplayBackend $DisplayBackend -Acceleration $Acceleration -MonitorResolution $MonitorResolution -DisplayCount $DisplayCount -Fullscreen:$Fullscreen -FitToWindow:$FitToWindow -NativePixels:$NativePixels -TraceConsoleRefresh:$TraceConsoleRefresh -PlanOnly:$PlanOnly
'@.Replace('__EDITION__',$Edition) | Set-Content -LiteralPath (Join-Path $package 'Start.ps1') -Encoding UTF8
@'
# XFESystem 独立内核 RAM 实验版
此包使用自研 C# 内核/HAL/调度与 Microsoft .NET 10 NativeAOT 运行时,不包含 Cosmos 内核或运行时。完整第三方声明位于 Licenses;自研范围和外部引用见 ThirdParty.md。
文件保存在内存中,退出或关机后消失。当前执行一个 BSP,CPU 软件绘制,x87/SSE 上下文;没有持久化存储或独立进程隔离。
Validation/Boot.json 记录本包同一镜像的真实 QEMU 桌面、PS/2 输入和浏览器 DOM 验收;BrowserAcceptancePassed 字段单独说明 Monaco 验收结果。Validation/Host.json 与逐项 TRX/原生测试记录保留宿主结果,宿主通过不能替代来宾执行。
Validation/Boot.json 与 Validation/Guest/BingEvidence.json(仅通过时携带)记录独立 QEMU 来宾的原生 Bing HTTPS 验收;包清单 BingNativeHttpsPassed/NetworkAcceptancePassed 只绑定这份真实证据,不由网卡启用状态推断。其范围仅 QEMU e1000 82540EM、显式静态 NAT 地址、验证证书的 Bing HTML/DOM 和实际像素呈现;不表示完整 CSS/JavaScript 或 Windows 浏览器像素一致。FullCssParityVerified 仍为 false。
这是 RAM 实验版。实体启动、实体 USB HID 输入、实体网络/DHCP、CUDA/GPU 和完整稳定性均未验收,不能标为完整发行版。CMOS RTC 按 UTC 解释;实体机的 RTC 配置和准确性尚未验收。
在 Windows 安装 QEMU 后执行:
powershell -NoProfile -ExecutionPolicy Bypass -File .\Start.ps1 -QemuPath "C:\Program Files\qemu\qemu-system-x86_64.exe"
默认打开 TCG / Q35 / x86_64 / 1 GiB 的 GTK 交互窗口,使用 Q35 内置 PS/2 键盘鼠标,RTC 设为 UTC。网络参数以已绑定的 NativeNetworkProfile 为准;启用时使用 QEMU user NAT/e1000 与显式静态地址,不使用宿主代理。可选择 -DisplayBackend sdl 或 -Fullscreen。启动前核验完整包、镜像、ELF、固件、验收记录及所有编译源码快照;每次复制镜像和固件变量到新的 build/IndependentKernel/Interactive-* 会话目录,串口日志位于同目录 Serial.log。关闭 QEMU 窗口结束。
源码与资源快照在 SourceSnapshot,宿主测试源码快照在 HostSourceSnapshot,所有文件 SHA256 在 Manifest.json。它们保存本次实际构建和测试的字节;完整复现命令与版本锁要求见 Reproduce.md。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
@'
# 复现与校验
在 XFESystem 仓库中使用 Manifest.json 的源码哈希核对 SourceSnapshot 中所有文件;保留仓库目录结构,按各 packages.lock.json 恢复固定依赖,工具版本锁位于 Tools/IndependentKernel/Runtime/NativeToolchain.json 与 Tools/CSharpBoot/BflatToolchain.json。
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1
python Tools/IndependentKernel/TestBoot.py <新生成的Kernel.elf> --virtual-cpus 8 --desktop --browser --documents --bing --timeout 360
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/TestCSharp.ps1
python Tools/IndependentKernel/CollectLicenses.py
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Package.ps1 -ImagePath <通过的System.img> -VerifiedBootResult <通过的Result.json> -VerifiedHostResult <宿主Result.json>
编译、链接成功不等于运行验收;每次新建产物需要新 guest/host 记录。本包原始精确编译命令保存在 Validation/BuildReceipt.json;完整包与携带的源码快照、固件可通过下列命令只读核验:
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Open.ps1 -ImagePath System.img -VerifiedBootResult Validation/Boot.json -ValidateOnly
SourceSnapshot 只保存实际编译输入,复现需要完整仓库中匹配的工具与锁文件。HostSourceSnapshot 保存实际宿主检查输入。各原始 JSON 的旧绝对路径用于出处记录;打包启动入口通过包内固定相对路径定位已验证文件。
'@ | Set-Content -LiteralPath (Join-Path $package 'Reproduce.md') -Encoding UTF8
if($Edition -eq 'Console') {
@'
# XFESystem Console 独立内核 RAM 实验版
这是独立构建的纯控制台系统,保留以前 Rust 控制台的黑底、彩色启动标题、文本提示符和键盘操作方式。系统内核仍为自研 C# 内核,运行时为 Microsoft .NET 10 NativeAOT;不启动图形桌面、窗口、浏览器或文档编辑器。
输入 help 查看命令。支持目录、RAM 文件读写、命令历史和光标编辑,也可运行内置 ConsoleIL 与 XFEConsole 示例。网络关闭。文件只保存在本次 RAM 会话,关机后消失。
在 Windows 安装 QEMU 后执行:
powershell -NoProfile -ExecutionPolicy Bypass -File .\Start.ps1 -QemuPath "C:\Program Files\qemu\qemu-system-x86_64.exe"
默认打开 TCG / Q35 / x86_64 / 1 GiB 的 GTK 控制台窗口,使用 Q35 内置 PS/2 键盘。可选择 -DisplayBackend sdl 或 -Fullscreen。启动前核验镜像、内核、固件、实际来宾命令执行证据和全部源码快照。每次启动使用独立镜像副本,串口日志在 build/IndependentKernel/Interactive-Console-* 下。
Validation/Boot.json 记录本包同一镜像的真实 QEMU 键盘输入、RAM 文件读写、命令历史、光标编辑与帧缓冲证据;Validation/Host.json 和逐项 TRX/原生记录保存宿主检查结果。第三方许可位于 Licenses,自研范围见 ThirdParty.md。源码及资源快照位于 SourceSnapshot,宿主检查源码位于 HostSourceSnapshot,文件 SHA256 位于 Manifest.json。
这是 RAM 实验版,实体启动与输入、持久存储、SMP、GPU/CUDA 和长期稳定性仍未验收,FullReleaseQualified=false。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
@'
# 复现与校验
从完整 XFESystem 仓库按锁文件恢复依赖,并核对包内 SourceSnapshot 与 Manifest.json 的源码哈希。
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/BuildSystem.ps1 -Edition Console
python Tools/IndependentKernel/TestBoot.py <新生成的Kernel.elf> --virtual-cpus 8 --console --timeout 360
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/TestCSharp.ps1
python Tools/IndependentKernel/CollectLicenses.py
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Package.ps1 -Edition Console -ImagePath <通过的System.img> -VerifiedBootResult <通过的Result.json> -VerifiedHostResult <宿主Result.json>
每次新建产物都需要新的 guest/host 记录。包内只读核验:
powershell -NoProfile -ExecutionPolicy Bypass -File Tools/IndependentKernel/Open.ps1 -Edition Console -ImagePath System.img -VerifiedBootResult Validation/Boot.json -ValidateOnly
Validation/BuildReceipt.json 保存精确编译命令。SourceSnapshot 只保存实际编译输入,复现仍需要完整仓库及匹配的工具和锁文件。JSON 内旧绝对路径用于出处记录,包内启动入口使用固定相对路径。
'@ | Set-Content -LiteralPath (Join-Path $package 'Reproduce.md') -Encoding UTF8
}
Assert-IndependentSourceMap $verified.Snapshot $taskRoot 'final guest source snapshot'
Assert-IndependentSourceMap $hostProof.SourceHashes $taskRoot 'final host source snapshot'
$files=@(foreach($file in Get-ChildItem -LiteralPath $package -File -Recurse | Sort-Object FullName) {
[ordered]@{Path=$file.FullName.Substring($package.Length+1).Replace('\','/');Bytes=$file.Length;Sha256=(Get-IndependentHash $file.FullName)}
})
if($verified.ImageManifest.UsbWorkspace -eq $true) {
@'
# XFESystem USB 工作区实验版
此包只包含所选的一个系统。启动 U 盘可用时,X: 的文件和外观设置提交到 Workspace.bin 的双数据区,UsbFiles 对应启动 FAT32 卷的 UserFiles 文件夹。
实体镜像使用自动显示模式。Desktop 的虚拟显示器默认 3840x2160,可用 -MonitorResolution 1280x800 改为较小的测试显示器;该参数不改写镜像。-Acceleration auto 在宿主已运行 Hypervisor 时选择 WHPX,否则选择 TCG,亦可显式指定 -Acceleration tcg 或 whpx;不会开启 Windows 系统功能。启动计划只记录请求参数,不能证明硬件加速或实体 240 Hz 已生效。Windows 流畅度共同标准见 DesktopFluidityStandard.md;实体扫描频率、VSync、多核执行和 Windows 流畅度一致尚未通过验收。
run X:\UsbFiles\UsbHello.dll 可运行 U 盘普通 FAT 文件中的标准 .NET 控制台示例,仍受 ConsoleIL 指令与 API 范围限制。Workspace.bin 为 64 MiB 容器,两个数据区交替提交;现有每文件及工作区配额继续生效。Temporary 和 System/Diagnostics 是临时数据。
没有唯一匹配启动分区的可用 USB BOT 设备时,明确回退到 RAM,会话文件不保留。验证范围为 QEMU xHCI/USB BOT,实体 UAS、实体启动输入和掉电行为仍需实测;这不是完整发行版。第三方来源与许可见 Licenses。
Start.ps1 使用独立的虚拟 USB 工作盘;系统原始镜像和验证记录保留。Validation/UsbStorage/Result.json 记录两次真实冷启动、文件和设置回读、FAT 程序加载与实际 framebuffer 证据。
其他磁盘位于 X:\Disks,图形版文件管理器的 Disks 按钮可打开。原生 NVMe、AHCI SATA 和其他 USB BOT 磁盘支持 GPT / MBR(含逻辑分区),普通 NTFS、FAT、exFAT 数据分区可创建、读写、修改、重命名和删除文件。终端可 cd /Disks 后 ls,进入对应分区执行文件命令;run 可读取其中兼容 ConsoleIL 的标准 .NET 控制台程序集。当前单文件读写上限 4 MiB。启动时发现并挂载磁盘,其他磁盘的在线热插拔重挂载尚未实现。
EFI / 恢复等保护分区只读;脏卷或休眠 Windows 卷只读,BitLocker 不解密。每次修改设置脏标记并同步缓存,失败后保留标记;没有实现 Windows $LogFile 回放或掉电原子事务。不会格式化、分区、TRIM 或安装到其他磁盘。实体控制器、真实硬盘持久化及掉电恢复仍未验收。文件系统使用明确声明的 DiscUtils 外部库;驱动与分区边界由本项目实现。
'@ | Set-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
if($verified.MultiDisplay) {
@'
显示器管理:打开 Settings > Display(或 Win+P),选择屏幕编号,切换 Duplicate / Extend / Show only the main display。Extend 下拖动编号块调整排列,选中屏幕后可设为主屏;Apply 后必须在 15 秒内 Keep changes,否则恢复原布局。Identify 在每个实际输出上显示各自编号。确认后的布局保存到启动 U 盘,硬件拓扑变化时使用安全默认布局。
虚拟机双屏启动:Start.ps1 -DisplayCount 2 -QemuPath <已安装的QEMU路径>。实体屏幕需在启动前连接,当前只管理固件交付的独立帧缓冲;分辨率和全局缩放沿用启动模式,没有原生 GPU 端口热插拔、运行中硬件模式切换或逐屏 DPI。两块独立虚拟显卡的三次冷启动和实际双输出验收位于 Validation/MultiDisplay;不能等同实体显卡全部接口可用。
'@ | Add-Content -LiteralPath (Join-Path $package 'Readme.md') -Encoding UTF8
}
$files=@(foreach($file in Get-ChildItem -LiteralPath $package -File -Recurse | Sort-Object FullName) {
[ordered]@{Path=$file.FullName.Substring($package.Length+1).Replace('\','/');Bytes=$file.Length;Sha256=(Get-IndependentHash $file.FullName)}
})
}
$manifest=[ordered]@{Kind=$(if($storageLabel -ceq 'USB'){if($Edition -eq 'Console'){'XfeIndependentUsbConsolePackage'}else{'XfeIndependentUsbDesktopPackage'}}else{if($Edition -eq 'Console'){'XfeIndependentRamConsolePackage'}else{'XfeIndependentRamDesktopPackage'}});Edition=$Edition;Version=$Version;ContainsCosmos=$false;
ResolutionPolicy=$(if($verified.ImageManifest.Resolution -ceq '0x0'){'AutomaticFirmwareMonitorMode'}else{'FixedVirtualTestMode'});WindowsFluidityParityPassed=$false;Physical240HzVerified=$false;
RamOnly=[bool]$verified.Boot.RamOnly;StorageEnabled=[bool]$verified.Boot.StorageEnabled;FullReleaseQualified=$false;PhysicalHardwareAcceptancePassed=$false;
UsbStorageAcceptancePassed=[bool]$verified.Boot.UsbStorageAcceptancePassed;
MultiDisplayAcceptancePassed=[bool]$verified.Boot.MultiDisplayAcceptancePassed;DisplayHotplugAcceptancePassed=$false;
OtherDiskAccessEnabled=[bool]$verified.Build.Configuration.OtherDiskAccessEnabled;OtherDiskAcceptancePassed=[bool]$verified.Boot.OtherDiskAcceptancePassed;
UsbHidAcceptancePassed=$false;NetworkAcceptancePassed=($verified.BingNativeHttpsPassed -eq $true);GpuAcceptancePassed=$false;
BingNativeHttpsPassed=($verified.BingNativeHttpsPassed -eq $true);FullCssParityVerified=$false;
NetworkAcceptanceScope='Only image-bound QEMU e1000/static user NAT and validated native HTTPS Bing HTML/DOM/presentation when BingNativeHttpsPassed is true; physical NIC/DHCP/general web compatibility are unverified';
NativeNetworkProfile=$verified.Boot.NativeNetworkProfile;
GuestAcceptancePassed=$true;HostAcceptancePassed=$true;BrowserAcceptancePassed=[bool]$verified.Boot.BrowserAcceptancePassed;
NoRenderFailures=[bool]$verified.NoRenderFailures;MonacoRasterPaintCompleted=[bool]$verified.MonacoRasterPaintCompleted;
DocumentWorkflowPassed=[bool]$verified.DocumentWorkflowPassed;
TerminalWorkflowPassed=[bool]$verified.TerminalWorkflowPassed;ConsoleAcceptancePassed=[bool]$verified.ConsoleAcceptancePassed;
MonacoKeyboardEditedModel=[bool]$verified.MonacoKeyboardEditedModel;
ImageSha256=$verified.Boot.ImageSha256;KernelSha256=$verified.Boot.KernelSha256;
SourceSnapshotSha256=$verified.Boot.SourceSnapshotSha256;BootReceiptSha256=(Get-IndependentHash $verified.BootPath);
HostReceiptSha256=(Get-IndependentHash $hostPath);LicenseManifestSha256=(Get-IndependentHash $licensesPath);
Files=$files}
$manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $package 'Manifest.json') -Encoding UTF8
# Exercise the relocated package's own verifier before creating the ZIP.
$null=& (Join-Path $package 'Tools/IndependentKernel/Open.ps1') -Edition $Edition -ImagePath (Join-Path $package 'System.img') -VerifiedBootResult (Join-Path $package 'Validation/Boot.json') -ValidateOnly
Compress-Archive -Path (Join-Path $package '*') -DestinationPath $zipPath -CompressionLevel Optimal
Add-Type -AssemblyName System.IO.Compression.FileSystem
$expectedEntries=[Collections.Generic.Dictionary[string,object]]::new([StringComparer]::Ordinal)
foreach($file in $files){$expectedEntries.Add([string]$file.Path,$file)}
$expectedEntries.Add('Manifest.json',[pscustomobject]@{Bytes=(Get-Item -LiteralPath (Join-Path $package 'Manifest.json')).Length;
Sha256=(Get-IndependentHash (Join-Path $package 'Manifest.json'))})
$zip= [IO.Compression.ZipFile]::OpenRead($zipPath)
try {
foreach($entry in $zip.Entries) {
$name=$entry.FullName.Replace('\','/')
if($name.EndsWith('/')){continue}
if(!$expectedEntries.ContainsKey($name)){throw ('Unexpected or duplicate ZIP entry: '+$name)}
$expected=$expectedEntries[$name]
if($entry.Length -ne $expected.Bytes){throw ('ZIP entry length changed: '+$name)}
$stream=$entry.Open();$sha=[Security.Cryptography.SHA256]::Create()
try {$actual=[BitConverter]::ToString($sha.ComputeHash($stream)).Replace('-','').ToLowerInvariant()}
finally {$stream.Dispose();$sha.Dispose()}
if($actual -cne $expected.Sha256){throw ('ZIP readback SHA256 differs: '+$name)}
[void]$expectedEntries.Remove($name)
}
if($expectedEntries.Count){throw ('ZIP is missing files: '+($expectedEntries.Keys -join ', '))}
} finally {$zip.Dispose()}
$receipt=[ordered]@{Kind='XfeIndependentKernelPackageReceipt';Edition=$Edition;Passed=$true;PackageDirectory=$package;ZipPath=$zipPath;
ZipSha256=(Get-IndependentHash $zipPath);ManifestSha256=(Get-IndependentHash (Join-Path $package 'Manifest.json'));
ImageSha256=$verified.Boot.ImageSha256;KernelSha256=$verified.Boot.KernelSha256;GuestAcceptancePassed=$true;
HostAcceptancePassed=$true;RelocatedPackageVerificationPassed=$true;ZipReadbackPassed=$true;
NoRenderFailures=[bool]$verified.NoRenderFailures;MonacoRasterPaintCompleted=[bool]$verified.MonacoRasterPaintCompleted;
DocumentWorkflowPassed=[bool]$verified.DocumentWorkflowPassed;
TerminalWorkflowPassed=[bool]$verified.TerminalWorkflowPassed;ConsoleAcceptancePassed=[bool]$verified.ConsoleAcceptancePassed;
MonacoKeyboardEditedModel=[bool]$verified.MonacoKeyboardEditedModel;
NetworkAcceptancePassed=($verified.BingNativeHttpsPassed -eq $true);BingNativeHttpsPassed=($verified.BingNativeHttpsPassed -eq $true);
FullCssParityVerified=$false;
FullReleaseQualified=$false;FinishedUtc=[DateTime]::UtcNow.ToString('o')}
$receipt | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath ($package+'.json') -Encoding UTF8
Write-Host ('Independent RAM experiment package: '+$zipPath)
return [pscustomobject]$receipt
} catch {
[ordered]@{Kind='XfeIndependentKernelPackageReceipt';Edition=$Edition;Passed=$false;PackageDirectory=$package;Error=$_.Exception.Message} |
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath ($package+'.failure.json') -Encoding UTF8
throw
}