fix(gates): require secret-safe observer credentials

This commit is contained in:
Erik 2026-08-22 14:20:14 +02:00
parent 772526d0cd
commit fa3e7978ec
5 changed files with 36 additions and 22 deletions

View file

@ -505,9 +505,10 @@ The exact commands, commits, metrics, artifacts, and evidence limits are in the
ACE session, proving exact resource convergence and no stale maps/owners.
The implemented two-client gate proved the observer login and movement, but
ACE rejected the concurrent primary login when both used the available
account. The gate now accepts separate primary and observer credentials and
keeps them out of its artifacts; a distinct-account connected run must close
the authoritative nonzero `RemotePlayers` row.
account. The gate now requires dedicated observer environment credentials,
keeps them out of its artifacts, and permits independent character indexes;
a distinct-account connected run must close the authoritative nonzero
`RemotePlayers` row.
2. **EXTERNAL HARDWARE GATE.** Repeat the current clean-source RX 9070 XT
pack-off/Low/Medium/High/Auto × 1080p/1440p/4K × capped/uncapped matrix on
every other supported physical GPU class. The integrated-AMD Auto-to-retail

View file

@ -49,9 +49,11 @@ known from this audit. Campaign AR remains deliberately **not shipped** until
all three evidence classes below pass:
1. **Distinct-account remote player.** Run
`tools/run-connected-render-pack-remote-player-gate.ps1` with separate
primary and observer ACE credentials. Require a nonzero authoritative
`RemotePlayers` caster count plus a visible moving remote-player shadow.
`tools/run-connected-render-pack-remote-player-gate.ps1` with the primary
`ACDREAM_TEST_USER`/`ACDREAM_TEST_PASS` and distinct observer
`ACDREAM_TEST_OBSERVER_USER`/`ACDREAM_TEST_OBSERVER_PASS` environment
variables. Require a nonzero authoritative `RemotePlayers` caster count plus
a visible moving remote-player shadow.
The same-account attempt is recorded in
[`artifacts/atmospheric-rendering/connected-remote-player-20260822-140355/report.json`](../../artifacts/atmospheric-rendering/connected-remote-player-20260822-140355/report.json): the observer entered and moved, then ACE rejected the concurrent primary session. The corrected gate does not serialize credentials.
2. **Unavailable physical adapters.** On every other supported physical Vulkan

View file

@ -122,11 +122,14 @@ primary client then timed out waiting for `CharacterList`: ACE did not permit a
second simultaneous character from the same account. Both clients were closed
cleanly, and the failed row is not represented as renderer evidence.
The gate now accepts a separate observer account and password, keeps credentials
out of its report, fails fast on session failure, and records whether distinct
accounts were configured. The authoritative nonzero `RemotePlayers` caster row
and visible moving remote-player shadow therefore require only access to a
second account; no missing renderer or test-harness implementation remains.
The gate now reads separate observer credentials from
`ACDREAM_TEST_OBSERVER_USER`/`ACDREAM_TEST_OBSERVER_PASS`, rejects same-account
configuration before launch, keeps credentials out of its report, and fails
fast on session failure. Character indexes are independently selectable because
index zero on two accounts still names distinct characters; the entered-world
identity assertion is authoritative. The nonzero `RemotePlayers` caster row and
visible moving remote-player shadow therefore require only access to a second
account; no missing renderer or test-harness implementation remains.
## Final automated gate

View file

@ -240,10 +240,10 @@ Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath
{
string source = ReadTool(RemotePlayerScript);
Assert.Contains("PrimaryCharacterIndex = 0", source, StringComparison.Ordinal);
Assert.Contains("ObserverCharacterIndex = 1", source, StringComparison.Ordinal);
Assert.Contains("[string]$ObserverAccount", source, StringComparison.Ordinal);
Assert.Contains("[string]$ObserverPassword", source, StringComparison.Ordinal);
Assert.Contains("Primary and observer character indexes must differ", source, StringComparison.Ordinal);
Assert.Contains("ObserverCharacterIndex = 0", source, StringComparison.Ordinal);
Assert.Contains("[string]$ObserverAccount = $env:ACDREAM_TEST_OBSERVER_USER", source, StringComparison.Ordinal);
Assert.Contains("[string]$ObserverPassword = $env:ACDREAM_TEST_OBSERVER_PASS", source, StringComparison.Ordinal);
Assert.Contains("Remote-player shadow evidence requires distinct ACE accounts", source, StringComparison.Ordinal);
Assert.Contains("Get-ConnectedGateBinaryIdentity", source, StringComparison.Ordinal);
Assert.Contains("-CharacterIndex $ObserverCharacterIndex", source, StringComparison.Ordinal);
Assert.Contains("-CharacterIndex $PrimaryCharacterIndex", source, StringComparison.Ordinal);
@ -254,6 +254,14 @@ Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath
Assert.Contains("Restore-ConnectedRenderPackGateEnvironment $primaryState", source, StringComparison.Ordinal);
Assert.Contains("Restore-ConnectedRenderPackGateEnvironment $observerState", source, StringComparison.Ordinal);
int reportStart = source.IndexOf("$report = [pscustomobject][ordered]@{", StringComparison.Ordinal);
Assert.True(reportStart >= 0);
string reportSource = source[reportStart..];
Assert.DoesNotContain("$Account", reportSource, StringComparison.Ordinal);
Assert.DoesNotContain("$Password", reportSource, StringComparison.Ordinal);
Assert.DoesNotContain("$ObserverAccount", reportSource, StringComparison.Ordinal);
Assert.DoesNotContain("$ObserverPassword", reportSource, StringComparison.Ordinal);
string observer = ReadTool("connected-render-pack-remote-observer.route.txt");
AssertAppearsInOrder(
observer,

View file

@ -3,10 +3,10 @@ param(
[string]$Repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path,
[string]$Account = $env:ACDREAM_TEST_USER,
[string]$Password = $env:ACDREAM_TEST_PASS,
[string]$ObserverAccount,
[string]$ObserverPassword,
[string]$ObserverAccount = $env:ACDREAM_TEST_OBSERVER_USER,
[string]$ObserverPassword = $env:ACDREAM_TEST_OBSERVER_PASS,
[ValidateRange(0, [int]::MaxValue)][int]$PrimaryCharacterIndex = 0,
[ValidateRange(0, [int]::MaxValue)][int]$ObserverCharacterIndex = 1,
[ValidateRange(0, [int]::MaxValue)][int]$ObserverCharacterIndex = 0,
[switch]$SkipBuild,
[int]$LoginTimeoutSeconds = 90
)
@ -19,8 +19,9 @@ if ([string]::IsNullOrWhiteSpace($Account)) { $Account = 'testaccount' }
if ([string]::IsNullOrWhiteSpace($Password)) { $Password = 'testpassword' }
if ([string]::IsNullOrWhiteSpace($ObserverAccount)) { $ObserverAccount = $Account }
if ([string]::IsNullOrWhiteSpace($ObserverPassword)) { $ObserverPassword = $Password }
if ($PrimaryCharacterIndex -eq $ObserverCharacterIndex) {
throw 'Primary and observer character indexes must differ.'
if ([string]::Equals($Account, $ObserverAccount, [StringComparison]::OrdinalIgnoreCase)) {
throw ('Remote-player shadow evidence requires distinct ACE accounts. ' +
'Set ACDREAM_TEST_OBSERVER_USER and ACDREAM_TEST_OBSERVER_PASS.')
}
$startedUtc = [DateTime]::UtcNow.ToString('O')
@ -313,8 +314,7 @@ finally {
TrackedSourceStatus = if ($null -eq $binaryIdentity) { @() } else { @($binaryIdentity.SourceTrackedStatus) }
PrimaryCharacterIndex = $PrimaryCharacterIndex
ObserverCharacterIndex = $ObserverCharacterIndex
DistinctAccountsConfigured = -not [string]::Equals(
$Account, $ObserverAccount, [StringComparison]::OrdinalIgnoreCase)
DistinctAccountsConfigured = $true
Result = $result
Failure = $failure
}