외부 실증과 인증 화면 경계 보강

This commit is contained in:
Yun Chan 2026-08-12 15:01:21 +09:00
parent 68fd8f65a3
commit 64e1b2210d
7 changed files with 64 additions and 10 deletions

View file

@ -8,8 +8,9 @@ TLS WebSocket route. Full soak additionally captures a physical microphone throu
ffmpeg.
The runner never writes raw audio, cookie values, session IDs, transcripts, or
replies to its evidence. Its default soak duration is the contractual 50
minutes. Use ``--preflight-only`` for a route/auth/provider-readiness audit that
replies to its evidence. Its production soak duration is at least 3,120 seconds
so the concurrent proof window can retain a full 3,000-second intersection. Use
``--preflight-only`` for a route/auth/provider-readiness audit that
never enumerates or opens a microphone. Full soak cannot enumerate or capture a
microphone unless the operator passes ``--confirm-physical-capture`` explicitly.
@ -47,7 +48,8 @@ DEFAULT_ORIGIN = "https://vignette.chanpaca.net"
DEFAULT_COOKIE_ENV = "VIGNETTE_PUBLIC_SESSION_COOKIE"
DEFAULT_SESSION_ENV = "VIGNETTE_PUBLIC_VOICE_SESSION_ID"
DEFAULT_COOKIE_NAME = "__Host-vignette_sid"
DEFAULT_DURATION_SECONDS = 3_000.0
MIN_PRODUCTION_DURATION_SECONDS = 3_120.0
DEFAULT_DURATION_SECONDS = MIN_PRODUCTION_DURATION_SECONDS
DEFAULT_TURN_INTERVAL_SECONDS = 60.0
DEFAULT_HEARTBEAT_SECONDS = 20.0
DEFAULT_CAPTURE_SECONDS = 3.0
@ -902,7 +904,8 @@ def parser() -> argparse.ArgumentParser:
result = argparse.ArgumentParser(
description=(
"Physical-microphone and authenticated public /voice/ws soak. "
"The default duration is the contractual 50 minutes."
"Production requires at least 3,120 seconds so the concurrent "
"proof retains a 3,000-second common window."
)
)
result.add_argument("--preflight-only", action="store_true")
@ -961,6 +964,13 @@ def validate_args(args: argparse.Namespace, cli: argparse.ArgumentParser) -> Non
cli.error(str(exc))
if args.duration_seconds <= 0:
cli.error("--duration-seconds must be > 0")
if (
not args.preflight_only
and args.duration_seconds < MIN_PRODUCTION_DURATION_SECONDS
):
cli.error(
"--duration-seconds must be >= 3120 for a production physical soak"
)
if args.turn_interval_seconds <= 0:
cli.error("--turn-interval-seconds must be > 0")
if args.heartbeat_seconds <= 0:

View file

@ -47,7 +47,7 @@ class G7ExternalVoiceSoakTests(unittest.TestCase):
def setUpClass(cls) -> None:
cls.runner = load_runner()
def test_default_duration_is_contractual_fifty_minutes(self) -> None:
def test_default_duration_preserves_the_common_window_margin(self) -> None:
args = self.runner.parser().parse_args(
[
"--expected-stt-provider",
@ -62,10 +62,37 @@ class G7ExternalVoiceSoakTests(unittest.TestCase):
"Physical Microphone",
]
)
self.assertEqual(3_000.0, args.duration_seconds)
self.assertEqual(3_120.0, args.duration_seconds)
self.assertFalse(args.preflight_only)
self.assertFalse(args.confirm_physical_capture)
def test_full_soak_rejects_a_duration_without_overlap_margin(self) -> None:
cli = self.runner.parser()
args = cli.parse_args(
[
"--expected-stt-provider",
"local_whisper",
"--expected-stt-model",
"small",
"--expected-tts-provider",
"melotts",
"--expected-tts-model",
"melotts-korean",
"--microphone-device",
"Physical Microphone",
"--duration-seconds",
"3000",
]
)
with (
patch.object(cli, "error", side_effect=ValueError) as error,
self.assertRaises(ValueError),
):
self.runner.validate_args(args, cli)
error.assert_called_once_with(
"--duration-seconds must be >= 3120 for a production physical soak"
)
def test_public_url_injects_session_without_changing_target_metadata(self) -> None:
raw_session_id = "private-session-id"
url = self.runner.with_session_id(