# Generated from the reviewed release manifest. Per-user code/configuration install only. param( [string]$InstallDir, [ValidateSet('native', 'file')][string]$Store = 'native', [string]$Archive ) & { param($RequestedDir, $RequestedStore, $OfflineArchive) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest if ($env:OS -ne 'Windows_NT') { throw 'Use the Bash installer on macOS/Linux.' } $machineArchitecture = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } if ($machineArchitecture -ne 'AMD64') { throw 'This release supports Windows x64.' } $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $ownerSid = $identity.User if ($ownerSid.Value -in @('S-1-5-18','S-1-5-19','S-1-5-20')) { throw 'Install from your normal user account, not a system service.' } $version = '0.1.0' $archiveSha = 'c4fd176f661ddfd74da3f930d08e56b13f4c0c87c467dd76595f81374898c76b' $binarySha = 'db423cf13fecbbfc67ec7afff1410b95871ccd6348a84bbe2dd9131b61301596' $url = 'https://mcpbytes.com/downloads/mcpbytes-vault/0.1.0/mcpbytes-vault-0.1.0-windows-x64.zip' $vaultRoot = if ($RequestedDir) { $RequestedDir } else { Join-Path $env:LOCALAPPDATA 'MCPBytes\Vault' } if ($vaultRoot -notmatch '^[A-Za-z]:[\\/]' -or $vaultRoot -match '[\x00-\x1f]') { throw 'Choose an absolute local installation directory.' } $vaultRoot = [IO.Path]::GetFullPath($vaultRoot).TrimEnd('\') $userProfilePath = [Environment]::GetFolderPath('UserProfile') if ($vaultRoot -eq $userProfilePath -or ($vaultRoot + '\') -eq [IO.Path]::GetPathRoot($vaultRoot)) { throw 'Choose a dedicated directory, not your profile or drive root.' } $drive = New-Object IO.DriveInfo ([IO.Path]::GetPathRoot($vaultRoot)) if ($drive.DriveFormat -notin @('NTFS','ReFS')) { throw 'A local filesystem with persistent Windows ACLs is required.' } function Assert-PlainPath([string]$Path) { $item = Get-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue if ($null -ne $item -and ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) { throw 'Refusing a reparse point in an installation/data path.' } } function Set-PrivateDirectory([string]$Path) { Assert-PlainPath $Path [IO.Directory]::CreateDirectory($Path) | Out-Null $acl = New-Object Security.AccessControl.DirectorySecurity $acl.SetOwner($ownerSid) $acl.SetAccessRuleProtection($true, $false) $rule = New-Object Security.AccessControl.FileSystemAccessRule($ownerSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow') $acl.AddAccessRule($rule) # PowerShell 5.1's provider can try to persist audit information and require # SeSecurityPrivilege. Persist only the changed owner/access sections through .NET. $directoryInfo = New-Object IO.DirectoryInfo $Path if ($PSVersionTable.PSVersion.Major -lt 6) { $directoryInfo.SetAccessControl($acl) } else { [IO.FileSystemAclExtensions]::SetAccessControl($directoryInfo, $acl) } } function Write-NewJson([string]$Path, $Value) { $bytes = (New-Object Text.UTF8Encoding($false)).GetBytes(($Value | ConvertTo-Json -Depth 10) + "`n") $stream = [IO.File]::Open($Path, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None) try { $stream.Write($bytes, 0, $bytes.Length); $stream.Flush($true) } finally { $stream.Dispose() } } function Hash-File([string]$Path) { $stream = [IO.File]::OpenRead($Path) $algorithm = [Security.Cryptography.SHA256]::Create() try { [BitConverter]::ToString($algorithm.ComputeHash($stream)).Replace('-', '').ToLowerInvariant() } finally { $algorithm.Dispose(); $stream.Dispose() } } Assert-PlainPath $vaultRoot if (Test-Path -LiteralPath $vaultRoot -PathType Container) { $directoryInfo = New-Object IO.DirectoryInfo $vaultRoot $existingAcl = if ($PSVersionTable.PSVersion.Major -lt 6) { $directoryInfo.GetAccessControl([Security.AccessControl.AccessControlSections]::Owner) } else { [IO.FileSystemAclExtensions]::GetAccessControl($directoryInfo, [Security.AccessControl.AccessControlSections]::Owner) } $existingOwner = $existingAcl.GetOwner([Security.Principal.SecurityIdentifier]) if ($existingOwner.Value -ne $ownerSid.Value) { throw 'Existing installation directory must belong to this user.' } } $marker = Join-Path $vaultRoot '.mcpbytes-vault-managed' $configPath = Join-Path $vaultRoot 'config.json' if ((Test-Path -LiteralPath $vaultRoot) -and !(Test-Path -LiteralPath $marker) -and !(Test-Path -LiteralPath $configPath)) { if (@(Get-ChildItem -LiteralPath $vaultRoot -Force).Count -ne 0) { throw 'Refusing a nonempty unmanaged directory.' } } Set-PrivateDirectory $vaultRoot foreach ($name in @('.mcpbytes-vault-managed','config.json','mcp-server.json','install.json','bin','versions','state','keys')) { Assert-PlainPath (Join-Path $vaultRoot $name) } foreach ($file in @($configPath, (Join-Path $vaultRoot 'mcp-server.json'), $marker)) { if ((Test-Path -LiteralPath $file) -and !(Test-Path -LiteralPath $file -PathType Leaf)) { throw 'A managed file path is occupied by a directory.' } } if ((Test-Path -LiteralPath $marker) -and (Get-Content -LiteralPath $marker -Raw).Trim() -ne 'MCPBytes Vault installer v1') { throw 'Unrecognized installation marker.' } $lockPath = Join-Path $vaultRoot '.install-lock' Assert-PlainPath $lockPath $lock = [IO.File]::Open($lockPath, [IO.FileMode]::OpenOrCreate, [IO.FileAccess]::ReadWrite, [IO.FileShare]::None) $scratch = $null try { if (!(Test-Path -LiteralPath $marker)) { [IO.File]::WriteAllText($marker, "MCPBytes Vault installer v1`n", (New-Object Text.UTF8Encoding($false))) } $binDir = Join-Path $vaultRoot 'bin'; Set-PrivateDirectory $binDir $versionsDir = Join-Path $vaultRoot 'versions'; Set-PrivateDirectory $versionsDir $scratch = Join-Path $vaultRoot ('.install-tmp.' + [Guid]::NewGuid().ToString('N')) Set-PrivateDirectory $scratch $zipPath = Join-Path $scratch 'release.zip' if ($OfflineArchive) { Assert-PlainPath $OfflineArchive if (!(Test-Path -LiteralPath $OfflineArchive -PathType Leaf)) { throw 'Offline archive must be a regular file.' } Copy-Item -LiteralPath $OfflineArchive -Destination $zipPath } else { Write-Host "Downloading MCPBytes Vault $version..." $previousTls = [Net.ServicePointManager]::SecurityProtocol try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $zipPath -MaximumRedirection 0 -TimeoutSec 120 } finally { [Net.ServicePointManager]::SecurityProtocol = $previousTls } } if ((Hash-File $zipPath) -cne $archiveSha) { throw 'Archive checksum mismatch; nothing was activated.' } Add-Type -AssemblyName System.IO.Compression.FileSystem $zip = [IO.Compression.ZipFile]::OpenRead($zipPath) try { if ($zip.Entries.Count -gt 4096) { throw 'Unexpected archive entry count.' } $total = 0L $seen = New-Object 'Collections.Generic.HashSet[string]' ([StringComparer]::OrdinalIgnoreCase) foreach ($entry in $zip.Entries) { $name = $entry.FullName if ($name.StartsWith('/') -or $name.Contains('\') -or $name.Contains(':') -or $name -match '(^|/)\.\.(/|$)|[\x00-\x1f]' -or !$seen.Add($name)) { throw 'Unsafe or ambiguous archive path.' } if ((($entry.ExternalAttributes -shr 16) -band 0xf000) -eq 0xa000) { throw 'Archive symlinks are not allowed.' } $total += $entry.Length } if ($total -gt 100MB) { throw 'Unexpected uncompressed archive size.' } } finally { $zip.Dispose() } $unpacked = Join-Path $scratch 'unpacked' [IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $unpacked) $unpackedBinary = Join-Path $unpacked 'mcpbytes-vault.exe' if ((Hash-File $unpackedBinary) -cne $binarySha) { throw 'Executable checksum mismatch.' } $versionDir = Join-Path $versionsDir $version Assert-PlainPath $versionDir if (Test-Path -LiteralPath $versionDir) { $existingVersionBinary = Join-Path $versionDir 'mcpbytes-vault.exe' Assert-PlainPath $existingVersionBinary if (!(Test-Path -LiteralPath $existingVersionBinary -PathType Leaf) -or (Hash-File $existingVersionBinary) -cne $binarySha) { throw 'Existing version differs from the release; preserve it for inspection.' } } else { if (![IO.Path]::GetFullPath($versionDir).StartsWith($vaultRoot + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Version destination escaped the installation root.' } Move-Item -LiteralPath $unpacked -Destination $versionDir } if (!(Test-Path -LiteralPath $configPath)) { $stateDir = Join-Path $vaultRoot 'state'; Set-PrivateDirectory $stateDir $storeConfig = [ordered]@{ backend = 'windows_credential_manager' } if ($RequestedStore -eq 'file') { $keysDir = Join-Path $vaultRoot 'keys'; Set-PrivateDirectory $keysDir $storeConfig = [ordered]@{ backend = 'private_file'; directory = $keysDir } } $config = [ordered]@{ state_dir = $stateDir; store = $storeConfig; label_prefix = 'agent-'; mode = 'remote_required'; remote = [ordered]@{ url = 'https://api.mcpbytes.com/v1/sealed-random'; api_key_env = 'MCPBYTES_API_KEY'; pins = @([ordered]@{ public_key = 'ZeUTSjyq45pq5CLe8ab0jc1e4L0QTD1CvQpchQiASzI'; firmware_digests = @('fUNTPVs4pZ_AyDDV7J06eyMOQ-y8l56z3CEt9GNnAQM') }) } } Write-NewJson $configPath $config Write-Host ('Created private configuration (' + $storeConfig.backend + ').') } else { Write-Host 'Existing configuration, pins and state preserved; -Store applies only on first install.' } $binaryPath = Join-Path $binDir 'mcpbytes-vault.exe' Assert-PlainPath $binaryPath $receiptPath = Join-Path $vaultRoot 'install.json' $currentHash = if (Test-Path -LiteralPath $binaryPath) { Hash-File $binaryPath } else { '' } if ($currentHash -and $currentHash -cne $binarySha) { if (!(Test-Path -LiteralPath $receiptPath -PathType Leaf)) { throw 'Refusing to overwrite an unmanaged executable.' } $previous = Get-Content -LiteralPath $receiptPath -Raw | ConvertFrom-Json if ($previous.binary_sha256 -cne $currentHash) { throw 'Installed executable was modified; preserve it for inspection.' } } if ($currentHash -cne $binarySha) { $stagedBinary = Join-Path $binDir ('.vault-' + [Guid]::NewGuid().ToString('N') + '.new') Copy-Item -LiteralPath (Join-Path $versionDir 'mcpbytes-vault.exe') -Destination $stagedBinary try { if (Test-Path -LiteralPath $binaryPath) { [IO.File]::Replace($stagedBinary, $binaryPath, (Join-Path $scratch 'previous-binary.exe')) } else { [IO.File]::Move($stagedBinary, $binaryPath) } } finally { if (Test-Path -LiteralPath $stagedBinary) { Remove-Item -LiteralPath $stagedBinary } } } $settingsPath = Join-Path $vaultRoot 'mcp-server.json' if (!(Test-Path -LiteralPath $settingsPath)) { Write-NewJson $settingsPath ([ordered]@{mcpServers=[ordered]@{'mcpbytes-vault'=[ordered]@{command=$binaryPath;args=@('--config',$configPath)}}}) } $receiptTemp = Join-Path $scratch 'install.json' Write-NewJson $receiptTemp ([ordered]@{version=$version;archive_sha256=$archiveSha;binary_sha256=$binarySha}) if (Test-Path -LiteralPath $receiptPath) { [IO.File]::Replace($receiptTemp, $receiptPath, (Join-Path $scratch 'previous-receipt.json')) } else { [IO.File]::Move($receiptTemp, $receiptPath) } Write-Host "`nInstalled MCPBytes Vault $version" Write-Host "Command: $binaryPath" Write-Host "Arguments: --config `"$configPath`"" Write-Host "MCP settings: $settingsPath" $quotedBinary = "'" + $binaryPath.Replace("'", "''") + "'" $quotedConfig = "'" + $configPath.Replace("'", "''") + "'" Write-Host "`nClaude Code (PowerShell):" # Avoid npm's .ps1 shim: PowerShell consumes its -- separator before forwarding arguments. Write-Host "& (Get-Command claude -CommandType Application | Select-Object -First 1 -ExpandProperty Source) mcp add --transport stdio --scope user mcpbytes-vault -- $quotedBinary --config $quotedConfig" Write-Host "`nCodex (PowerShell):" Write-Host "& (Get-Command codex -CommandType Application | Select-Object -First 1 -ExpandProperty Source) mcp add mcpbytes-vault -- $quotedBinary --config $quotedConfig" Write-Host 'Then add env_vars = ["MCPBYTES_API_KEY"] to [mcp_servers.mcpbytes-vault] in ~/.codex/config.toml.' Write-Host 'Cursor, VS Code, Gemini, OpenCode, Pi and other local clients: https://mcpbytes.com/docs/random-bytes#connect' Write-Host 'Set MCPBYTES_API_KEY privately in your MCP client environment, then connect this local stdio server.' Write-Host 'No API key was collected, no key material was generated, and no client settings or PATH were changed.' } finally { try { if ($scratch -and (Test-Path -LiteralPath $scratch)) { $resolvedScratch = [IO.Path]::GetFullPath($scratch) if (!$resolvedScratch.StartsWith($vaultRoot + '\.install-tmp.', [StringComparison]::OrdinalIgnoreCase)) { throw 'Unsafe temporary cleanup path.' } Assert-PlainPath $resolvedScratch Remove-Item -LiteralPath $resolvedScratch -Recurse -Force } } finally { $lock.Dispose() } # Keep the empty lock file: deleting it could race another installer that just acquired it. } } $InstallDir $Store $Archive