param( [string]$OutputDir = "runs/send-sandbox-gate-package", [switch]$NoZip ) $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest $repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path $packageDir = Join-Path $repo $OutputDir $recorderRelative = Join-Path $OutputDir "recorder" $recorderDir = Join-Path $packageDir "recorder" $zipPath = "$packageDir.zip" if (Test-Path -LiteralPath $packageDir) { Remove-Item -LiteralPath $packageDir -Recurse -Force } if (Test-Path -LiteralPath $zipPath) { Remove-Item -LiteralPath $zipPath -Force } New-Item -ItemType Directory -Force -Path $packageDir | Out-Null & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repo "scripts\package-live-probe-recorder.ps1") -OutputDir $recorderRelative -NoZip if ($LASTEXITCODE -ne 0) { throw "package-live-probe-recorder.ps1 failed with exit code $LASTEXITCODE" } if (-not (Test-Path -LiteralPath (Join-Path $recorderDir "ISphereLiveProbeRecorder.exe"))) { throw "recorder package was not created under $recorderDir" } @' R6c iSphere Send Sandbox Gate Package LOCAL_LOGIN_UNAVAILABLE: the development machine cannot log in to iSphere. ONLINE_SANDBOX_REQUIRED: run this package only on a machine/network where iSphere/IMPlatformClient can log in. Goal: - collect one operator-approved sandbox-send evidence package; - keep production send disabled in the repository; - return enough evidence to decide whether production send work can start later. What this package does: - runs the read-only live probe recorder before the manual sandbox send; - asks the operator to manually send exactly one sandbox message in the official client; - runs the same read-only recorder after the manual send; - zips the returned evidence folder. What this package does not do: - it does not send messages automatically; - it does not click or type; - it does not upload files; - it does not hook or inject into the client; - it does not enable production send. Required rule: - MANUAL_ONE_SEND_ONLY: send exactly one sandbox message manually. - DO_NOT_SEND_SECOND_TIME: do not send a second message with the same content or idempotency key. - Record content_sha256 and idempotency_key in SANDBOX-SEND-INPUTS.json. - Fill success_ack_or_sent_record after the operator sees the sent message, ack, or local sent record. Return: - run make-return-zip.ps1 and send back the generated zip. '@ | Set-Content -LiteralPath (Join-Path $packageDir "README.txt") -Encoding UTF8 @' # R6c Online Sandbox Send Gate - Operator Steps This package is for an online/internal sandbox only. Markers: - LOCAL_LOGIN_UNAVAILABLE: do not run this on the local development machine for proof. - ONLINE_SANDBOX_REQUIRED: run on a machine where iSphere/IMPlatformClient can log in. - MANUAL_ONE_SEND_ONLY: send exactly one test message manually. - DO_NOT_SEND_SECOND_TIME: do not repeat the send with the same idempotency_key. ## Step 1 - Prepare inputs 1. Copy `SANDBOX-SEND-INPUTS.template.json` to `SANDBOX-SEND-INPUTS.json`. 2. Fill: - `sandbox_target_type` - `sandbox_target_id` - `content_text_to_send` - `idempotency_key` 3. Run: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File .\compute-content-hash.ps1 -Text "" ``` 4. Copy the printed SHA256 into `content_sha256`. ## Step 2 - Before-send recording 1. Open and log in to iSphere/IMPlatformClient normally. 2. Open the sandbox target conversation. 3. Do not send anything yet. 4. Run `run-before-recorder.bat`. ## Step 3 - Manual one-send action 1. In the official iSphere client, manually send exactly the `content_text_to_send`. 2. Send it only to the declared sandbox target. 3. Do not send a second copy. 4. Fill these fields in `SANDBOX-SEND-INPUTS.json`: - `manual_send_started_at_local` - `manual_send_finished_at_local` - `operator_observed_success` - `success_ack_or_sent_record` - `do_not_send_second_time_confirmed` ## Step 4 - After-send recording Run `run-after-recorder.bat`. ## Step 5 - Return package Run: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File .\make-return-zip.ps1 ``` Send back the generated zip file. ## Required returned evidence The returned evidence should contain: - before recorder output; - after recorder output; - completed `SANDBOX-SEND-INPUTS.json`; - success_ack_or_sent_record text; - confirmation that there was no second send. The repository must keep production send disabled until this evidence is reviewed. '@ | Set-Content -LiteralPath (Join-Path $packageDir "OPERATOR-STEPS.md") -Encoding UTF8 @' { "gate": "R6c online sandbox-send evidence gate", "LOCAL_LOGIN_UNAVAILABLE": true, "ONLINE_SANDBOX_REQUIRED": true, "MANUAL_ONE_SEND_ONLY": true, "DO_NOT_SEND_SECOND_TIME": true, "sandbox_target_type": "direct-or-group", "sandbox_target_id": "", "content_text_to_send": "", "content_sha256": "", "idempotency_key": "", "manual_send_started_at_local": "", "manual_send_finished_at_local": "", "operator_observed_success": false, "success_ack_or_sent_record": "", "do_not_send_second_time_confirmed": false, "notes": "" } '@ | Set-Content -LiteralPath (Join-Path $packageDir "SANDBOX-SEND-INPUTS.template.json") -Encoding UTF8 @' Expected return files for R6c: 1. SANDBOX-SEND-INPUTS.json - content_sha256 is filled. - idempotency_key is filled. - success_ack_or_sent_record is filled. - MANUAL_ONE_SEND_ONLY is true. - DO_NOT_SEND_SECOND_TIME is true. - do_not_send_second_time_confirmed is true. 2. return-evidence/before/* - Read-only recorder output before manual send. 3. return-evidence/after/* - Read-only recorder output after manual send. 4. The generated zip from make-return-zip.ps1. Decision rule: - If success_ack_or_sent_record is missing, production send remains blocked. - If duplicate/idempotency confirmation is missing, production send remains blocked. - If the target was not an explicit sandbox target, production send remains blocked. '@ | Set-Content -LiteralPath (Join-Path $packageDir "EXPECTED-RETURN-FILES.txt") -Encoding UTF8 @' @echo off setlocal cd /d "%~dp0" mkdir return-evidence 2>nul mkdir return-evidence\before 2>nul echo Running BEFORE read-only recorder... recorder\ISphereLiveProbeRecorder.exe --record-live-probe --out "%~dp0return-evidence\before" --no-pause echo Done. Check return-evidence\before. pause '@ | Set-Content -LiteralPath (Join-Path $packageDir "run-before-recorder.bat") -Encoding ASCII @' @echo off setlocal cd /d "%~dp0" mkdir return-evidence 2>nul mkdir return-evidence\after 2>nul echo Running AFTER read-only recorder... recorder\ISphereLiveProbeRecorder.exe --record-live-probe --out "%~dp0return-evidence\after" --no-pause echo Done. Check return-evidence\after. pause '@ | Set-Content -LiteralPath (Join-Path $packageDir "run-after-recorder.bat") -Encoding ASCII @' param( [Parameter(Mandatory=$true)] [string]$Text ) $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text) $sha = [System.Security.Cryptography.SHA256]::Create() try { $hash = $sha.ComputeHash($bytes) -join ($hash | ForEach-Object { $_.ToString("x2") }) } finally { $sha.Dispose() } '@ | Set-Content -LiteralPath (Join-Path $packageDir "compute-content-hash.ps1") -Encoding ASCII @' $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest $root = Split-Path -Parent $MyInvocation.MyCommand.Path $inputs = Join-Path $root "SANDBOX-SEND-INPUTS.json" if (-not (Test-Path -LiteralPath $inputs)) { throw "missing SANDBOX-SEND-INPUTS.json; copy the template and fill it first" } $before = Join-Path $root "return-evidence\before" $after = Join-Path $root "return-evidence\after" if (-not (Test-Path -LiteralPath $before)) { throw "missing before recorder output: $before" } if (-not (Test-Path -LiteralPath $after)) { throw "missing after recorder output: $after" } $stamp = Get-Date -Format "yyyyMMdd-HHmmss" $zip = Join-Path $root ("isphere-send-sandbox-return-" + $stamp + ".zip") $items = @( $inputs, (Join-Path $root "return-evidence"), (Join-Path $root "EXPECTED-RETURN-FILES.txt") ) Compress-Archive -Path $items -DestinationPath $zip -Force [ordered]@{ ok = $true zip = $zip production_send_enabled = $false online_sandbox_required = $true } | ConvertTo-Json -Depth 4 -Compress '@ | Set-Content -LiteralPath (Join-Path $packageDir "make-return-zip.ps1") -Encoding ASCII if (-not $NoZip) { Compress-Archive -Path (Join-Path $packageDir "*") -DestinationPath $zipPath -Force } [ordered]@{ ok = $true package_dir = (Resolve-Path -LiteralPath $packageDir).Path recorder_dir = (Resolve-Path -LiteralPath $recorderDir).Path zip = if (Test-Path -LiteralPath $zipPath) { (Resolve-Path -LiteralPath $zipPath).Path } else { $null } local_login_required = $false online_sandbox_required = $true production_send_enabled = $false } | ConvertTo-Json -Depth 4 -Compress