G8 실제 rollback 증명 종료와 비-secure origin 회기 리뷰 크래시 수정

G8 마지막 게이트인 receipt-bound 실제 image rollback을 격리 NAS
vignette-preview-20260807 에서 실행해 종료했다.

Gate6 계약 정정:
감사 대상 current API 이미지가 com.docker.compose.project/service/version
image label 을 갖고 있어 "helper 의 compose label 0개" 계약은 감사되지 않은
다른 이미지를 쓰지 않는 한 성립하지 않는다. 계약을 key 부재가 아니라
소속(membership) 으로 바꿔 launch-nas-preview-g8-helpers.py 에 구현했다.
image 상속 label 을 baseline 으로 읽고 container 의 모든 compose label 이
baseline 과 같거나 선언된 격리 override 인지 검사하며, 최종 project 는
target 이 아니고 service 는 api/web/db/proxy 가 아니어야 한다. docker run
argv 에 target label 을 주입하면 fake-runner 테스트가 먼저 깨진다 (37/37).

실행 결과:
- rollback-old  receipt nas-g8-723eeef22eab05e63e3fafb0 -> 79ec../c530..
- restore-current receipt nas-g8-2738846cf2cf4fbe8ce0fc26 -> 52e0../6fdb..
- release gate/approval 각 2회 멱등, audit.ci_lifecycle_event rollback/executed 2,
  audit.ci_human_approval_event authorize_rollback 2, silent auto-promotion 0
- HMAC journal 6-record 체인 검증, health 3/3, OpenAPI 126, auth 401, Web 200
- helper 0, listener 0, 비밀 env 파기. down/volume rm/prune 미실행, 공개 런타임 미접촉
- 계획했던 Windows SSH 터널은 NAS sshd 가 direct-tcpip 를 거부해 사용할 수 없어
  sshd 설정 변경 대신 같은 격리 계약의 NAS-side probe 컨테이너로 실행했다

비-secure origin 크래시 수정:
배포된 NAS 프리뷰(평문 HTTP, 비-localhost)에 회기 스펙을 돌려 24건 실패를 확인했고
원인은 하나였다. crypto.randomUUID 는 secure context 전용인데 제품 코드 18곳이
fallback 없이 호출했고 RuptureRepairCard 는 렌더 시점 호출이라 회기 리뷰 라우트
전체가 error boundary 로 떨어졌다. 릴리스 게이트 108/108 은 localhost 후보 스택에서만
돌아 이 경로를 밟은 적이 없다. src/lib/uuid.ts 의 randomUuid() 로 통일하고 fallback 도
crypto.getRandomValues 를 우선 사용해 idempotency key 의 예측 불가능성을 유지했다.
회귀는 insecure-context-uuid.spec.ts 6/6 으로 고정했다(직접 호출 0건 검사 포함).
이 수정은 아직 NAS 에 배포하지 않았다.

검증:
API 898, gateway 58, executor 28, probe 11, helper launcher 37, release agent 21,
ruff clean, web api-types/typecheck/build, SSOT FAIL 0, SSOT unit 5/5,
dashboard E2E 10/10, 학생 폐루프 실 DB 브라우저 4/4(일회용 클론),
crypto 수정 후 기존 스펙 회귀 70/70, 복원된 NAS 실제 브라우저 SSE->DB 리뷰 PASS.

부수 발견(열린 항목):
공개 API 가 engine=false 로 degraded 인데 워치독이 이를 감지하지 못한다. engine 판정이
게이트웨이 /health 의 ok 만 보고 claude readiness probe 를 돌리지 않기 때문이다.
같은 .env 와 같은 CLI 로 새 게이트웨이를 다른 포트에 띄우면 즉시 ready 이므로 상주
프로세스의 세션만 죽은 형태다. TODO A절과 대시보드에 기록했다.

이 커밋은 파일 단위로 담겼다. 위 파일들에는 이전 세션의 미커밋 G0~G8 작업이 함께
들어 있으며, hunk 를 쪼개면 대시보드/체커/TODO 정합성이 깨져 SSOT 체커가 실패한다.
This commit is contained in:
Yun Chan 2026-08-07 22:17:20 +09:00
parent 76d0b9ae9b
commit 93dd8f82d7
22 changed files with 10057 additions and 473 deletions

View file

@ -0,0 +1,534 @@
from __future__ import annotations
import importlib.util
import json
import sys
import unittest
from pathlib import Path
SCRIPT_PATH = Path(__file__).with_name("launch-nas-preview-g8-helpers.py")
SPEC = importlib.util.spec_from_file_location(
"launch_nas_preview_g8_helpers", SCRIPT_PATH
)
assert SPEC is not None and SPEC.loader is not None
MODULE = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = MODULE
SPEC.loader.exec_module(MODULE)
RUN_ID = "20260807a1"
IMAGE = "sha256:" + "5e" * 32
MANIFEST_SHA = "e" * 64
BASELINE_LABELS = {
"com.docker.compose.project": MODULE.TARGET_PROJECT,
"com.docker.compose.service": "api",
"com.docker.compose.version": "2.20.1",
}
def executor_spec() -> object:
return MODULE.executor_spec(
run_id=RUN_ID, image=IMAGE, manifest_sha256=MANIFEST_SHA
)
def control_plane_spec() -> object:
return MODULE.control_plane_spec(run_id=RUN_ID, image=IMAGE)
def probe_spec(plan_name: str = "rollback-old") -> object:
return MODULE.probe_spec(run_id=RUN_ID, image=IMAGE, plan_name=plan_name)
def container_labels(spec: object, **overrides: str) -> dict[str, str]:
labels = dict(BASELINE_LABELS)
labels.update(spec.isolated_labels)
labels.update(spec.provenance_labels)
labels.update(overrides)
return labels
class FakeRunner:
"""Record every argv and answer from a canned, operation-keyed map."""
def __init__(self, responses: dict[str, str] | None = None):
self.calls: list[tuple[str, list[str]]] = []
self.responses = responses or {}
def run(self, operation: str, argv: list[str], *, timeout: float) -> str:
assert isinstance(argv, list) and argv
assert all(isinstance(item, str) for item in argv)
assert timeout > 0
self.calls.append((operation, list(argv)))
return self.responses.get(operation, "")
def argv_for(self, operation: str) -> list[str]:
matches = [argv for name, argv in self.calls if name == operation]
assert len(matches) == 1, operation
return matches[0]
def _label_values(argv: list[str]) -> dict[str, str]:
values: dict[str, str] = {}
for index, item in enumerate(argv):
if item == "--label":
key, _, value = argv[index + 1].partition("=")
values[key] = value
return values
class SpecContractTest(unittest.TestCase):
def test_isolated_project_is_never_the_target_project(self) -> None:
for spec in (executor_spec(), control_plane_spec(), probe_spec()):
self.assertNotEqual(spec.isolated_project, MODULE.TARGET_PROJECT)
self.assertTrue(
spec.isolated_project.startswith(MODULE.HELPER_PROJECT_PREFIX)
)
def test_isolated_service_is_never_a_target_service(self) -> None:
for spec in (executor_spec(), control_plane_spec(), probe_spec()):
self.assertNotIn(spec.isolated_service, MODULE.TARGET_SERVICES)
def test_container_name_is_not_a_target_compose_container(self) -> None:
for spec in (executor_spec(), control_plane_spec(), probe_spec()):
for service in MODULE.TARGET_SERVICES:
self.assertNotEqual(
spec.container_name, f"{MODULE.TARGET_PROJECT}-{service}-1"
)
def test_invalid_run_id_and_image_fail_closed(self) -> None:
with self.assertRaises(MODULE.HelperError):
MODULE.control_plane_spec(run_id="BAD ID", image=IMAGE)
with self.assertRaises(MODULE.HelperError):
MODULE.control_plane_spec(run_id=RUN_ID, image="latest")
with self.assertRaises(MODULE.HelperError):
MODULE.executor_spec(run_id=RUN_ID, image=IMAGE, manifest_sha256="nope")
class RunArgvContractTest(unittest.TestCase):
def test_argv_never_labels_the_target_project_or_service(self) -> None:
for spec in (executor_spec(), control_plane_spec(), probe_spec()):
argv = MODULE.build_run_argv(spec)
labels = _label_values(argv)
self.assertEqual(
labels[MODULE.PROJECT_LABEL], spec.isolated_project
)
self.assertEqual(
labels[MODULE.SERVICE_LABEL], spec.isolated_service
)
self.assertNotIn(MODULE.TARGET_PROJECT, labels.values())
self.assertNotIn(labels[MODULE.SERVICE_LABEL], MODULE.TARGET_SERVICES)
def test_argv_declares_only_the_two_isolated_compose_labels(self) -> None:
argv = MODULE.build_run_argv(executor_spec())
compose_labels = {
key
for key in _label_values(argv)
if key.startswith(MODULE.COMPOSE_LABEL_PREFIX)
}
self.assertEqual(
compose_labels, {MODULE.PROJECT_LABEL, MODULE.SERVICE_LABEL}
)
def test_injected_target_project_label_is_rejected(self) -> None:
spec = executor_spec()
argv = MODULE.build_run_argv(spec)
tampered = argv + [
"--label",
f"{MODULE.PROJECT_LABEL}={MODULE.TARGET_PROJECT}",
]
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.assert_argv_isolation(tampered, spec)
self.assertEqual(ctx.exception.code, "helper_argv_targets_project")
def test_injected_target_service_label_is_rejected(self) -> None:
spec = control_plane_spec()
tampered = MODULE.build_run_argv(spec) + [
"--label",
f"{MODULE.SERVICE_LABEL}=api",
]
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.assert_argv_isolation(tampered, spec)
self.assertEqual(ctx.exception.code, "helper_argv_targets_service")
def test_undeclared_compose_label_is_rejected(self) -> None:
spec = executor_spec()
tampered = MODULE.build_run_argv(spec) + [
"--label",
f"{MODULE.COMPOSE_LABEL_PREFIX}config-hash=deadbeef",
]
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.assert_argv_isolation(tampered, spec)
self.assertEqual(ctx.exception.code, "helper_argv_undeclared_compose_label")
def test_label_file_and_non_host_network_are_rejected(self) -> None:
spec = executor_spec()
with self.assertRaises(MODULE.HelperError):
MODULE.assert_argv_isolation(
MODULE.build_run_argv(spec) + ["--label-file", "/tmp/labels"], spec
)
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.assert_argv_isolation(
[
"docker",
"run",
"--network",
f"{MODULE.TARGET_PROJECT}_vignette",
spec.image,
],
spec,
)
self.assertEqual(ctx.exception.code, "helper_argv_network_not_isolated")
def test_executor_argv_carries_the_hardening_flags(self) -> None:
argv = MODULE.build_run_argv(executor_spec())
self.assertIn("--read-only", argv)
self.assertIn("--cap-drop", argv)
self.assertEqual(argv[argv.index("--cap-drop") + 1], "ALL")
self.assertIn("--security-opt", argv)
self.assertEqual(
argv[argv.index("--security-opt") + 1], "no-new-privileges"
)
self.assertEqual(
argv[argv.index("--user") + 1],
f"{MODULE.HELPER_UID}:{MODULE.HELPER_GID}",
)
self.assertEqual(
argv[argv.index("--group-add") + 1],
str(MODULE.HELPER_SUPPLEMENTAL_GID),
)
self.assertEqual(argv[argv.index("--network") + 1], "host")
def test_executor_argv_mounts_target_root_read_only_and_state_rw(self) -> None:
argv = MODULE.build_run_argv(executor_spec())
mounts = [
argv[index + 1]
for index, item in enumerate(argv)
if item == "--mount"
]
self.assertIn(
f"type=bind,source={MODULE.TARGET_ROOT},"
f"target={MODULE.TARGET_ROOT},readonly",
mounts,
)
self.assertIn(
f"type=bind,source={MODULE.STATE_DIR},target={MODULE.STATE_DIR}",
mounts,
)
self.assertIn(
f"type=bind,source={MODULE.DOCKER_BINARY_SOURCE},"
f"target={MODULE.DOCKER_BINARY_TARGET},readonly",
mounts,
)
self.assertIn(
f"type=bind,source={MODULE.COMPOSE_PLUGIN_SOURCE},"
f"target={MODULE.COMPOSE_PLUGIN_TARGET},readonly",
mounts,
)
self.assertIn(
f"type=bind,source={MODULE.TARGET_DOCKER_SOCKET},"
f"target={MODULE.TARGET_DOCKER_SOCKET}",
mounts,
)
def test_executor_argv_binds_loopback_and_the_audited_manifest(self) -> None:
argv = MODULE.build_run_argv(executor_spec())
self.assertEqual(argv[argv.index("--entrypoint") + 1], "python3.11")
self.assertEqual(argv[argv.index("--host") + 1], MODULE.EXECUTOR_HOST)
self.assertEqual(argv[argv.index("--port") + 1], str(MODULE.EXECUTOR_PORT))
self.assertEqual(argv[argv.index("--manifest") + 1], str(MODULE.MANIFEST_PATH))
self.assertEqual(argv[argv.index("--manifest-sha256") + 1], MANIFEST_SHA)
self.assertIn("--enable", argv)
def test_control_plane_argv_binds_its_own_loopback_port(self) -> None:
argv = MODULE.build_run_argv(control_plane_spec())
self.assertEqual(argv[argv.index("--entrypoint") + 1], "uvicorn")
self.assertEqual(argv[argv.index("--host") + 1], MODULE.CONTROL_PLANE_HOST)
self.assertEqual(
argv[argv.index("--port") + 1], str(MODULE.CONTROL_PLANE_PORT)
)
self.assertNotIn("--publish", argv)
def test_argv_never_mutates_the_target_compose_project(self) -> None:
for spec in (executor_spec(), control_plane_spec(), probe_spec()):
argv = MODULE.build_run_argv(spec)
self.assertEqual(argv[1], "run")
for forbidden in ("compose", "down", "prune", "rm", "stop", "kill"):
self.assertNotIn(forbidden, argv[1:2])
self.assertNotIn(MODULE.TARGET_PROJECT, argv[3:])
class ProbeRoleTest(unittest.TestCase):
def test_probe_never_mounts_the_docker_socket(self) -> None:
argv = MODULE.build_run_argv(probe_spec())
mounts = [
argv[index + 1] for index, item in enumerate(argv) if item == "--mount"
]
self.assertFalse(
any(MODULE.TARGET_DOCKER_SOCKET in mount for mount in mounts)
)
self.assertNotIn("--group-add", argv)
def test_probe_targets_the_separate_control_plane_origin(self) -> None:
argv = MODULE.build_run_argv(probe_spec())
control = argv[argv.index("--control-plane-base-url") + 1]
preview = argv[argv.index("--preview-base-url") + 1]
self.assertEqual(control, MODULE.CONTROL_PLANE_BASE_URL)
self.assertEqual(preview, MODULE.PREVIEW_BASE_URL)
self.assertNotEqual(control, preview)
def test_probe_plan_and_output_stay_inside_the_state_dir(self) -> None:
for plan_name in ("rollback-old", "restore-current"):
argv = MODULE.build_run_argv(probe_spec(plan_name))
plan = argv[argv.index("--plan") + 1]
output = argv[argv.index("--output") + 1]
self.assertEqual(
plan, f"{MODULE.PLANS_DIR}/{plan_name}.plan.json"
)
self.assertTrue(output.startswith(f"{MODULE.EVIDENCE_DIR}/"))
self.assertIn("execute", argv)
def test_probe_plan_name_is_validated(self) -> None:
for bad in ("../escape", "Rollback", "a", "x" * 80):
with self.assertRaises(MODULE.HelperError):
MODULE.probe_spec(run_id=RUN_ID, image=IMAGE, plan_name=bad)
def test_wait_reports_the_exit_code(self) -> None:
spec = probe_spec()
runner = FakeRunner(
{
"wait_helper": "0\n",
"inspect_container_id": "b" * 64 + "\n",
"inspect_container_state": "false\n",
}
)
result = MODULE.HelperLauncher(runner).wait(spec, timeout_seconds=60)
self.assertEqual(result["exit_code"], 0)
self.assertFalse(result["container_running"])
self.assertEqual(
runner.argv_for("wait_helper"), ["docker", "wait", spec.container_name]
)
def test_wait_rejects_an_unbounded_timeout(self) -> None:
with self.assertRaises(MODULE.HelperError):
MODULE.HelperLauncher(FakeRunner()).wait(probe_spec(), timeout_seconds=0)
class IsolationVerificationTest(unittest.TestCase):
def test_inherited_baseline_labels_are_accepted(self) -> None:
spec = executor_spec()
evidence = MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=container_labels(spec),
target_project_members=[f"{MODULE.TARGET_PROJECT}-api-1"],
)
self.assertEqual(
evidence["inherited_compose_labels"],
{"com.docker.compose.version": "2.20.1"},
)
self.assertEqual(
evidence["isolated_override_labels"],
{
MODULE.PROJECT_LABEL: spec.isolated_project,
MODULE.SERVICE_LABEL: spec.isolated_service,
},
)
self.assertEqual(
evidence["baseline_compose_labels"]["com.docker.compose.project"],
MODULE.TARGET_PROJECT,
)
def test_zero_compose_labels_is_not_required(self) -> None:
spec = executor_spec()
labels = container_labels(spec)
self.assertTrue(
any(key.startswith(MODULE.COMPOSE_LABEL_PREFIX) for key in labels)
)
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=labels,
target_project_members=[],
)
def test_target_project_membership_is_rejected(self) -> None:
spec = executor_spec()
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=container_labels(
spec, **{MODULE.PROJECT_LABEL: MODULE.TARGET_PROJECT}
),
target_project_members=[],
)
self.assertEqual(ctx.exception.code, "helper_isolated_label_mismatch")
def test_target_service_claim_is_rejected(self) -> None:
spec = MODULE.HelperSpec(
role="executor",
run_id=RUN_ID,
image=IMAGE,
env_file=str(MODULE.STATE_DIR / "x.env"),
entrypoint="python3.11",
command=("-c", "pass"),
)
labels = container_labels(spec)
labels[MODULE.SERVICE_LABEL] = "db"
with self.assertRaises(MODULE.HelperError):
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=labels,
target_project_members=[],
)
def test_unexpected_compose_label_drift_is_rejected(self) -> None:
spec = executor_spec()
labels = container_labels(spec)
labels["com.docker.compose.version"] = "9.9.9"
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=labels,
target_project_members=[],
)
self.assertEqual(ctx.exception.code, "helper_compose_label_drift")
def test_missing_isolated_override_is_rejected(self) -> None:
spec = executor_spec()
labels = container_labels(spec)
del labels[MODULE.SERVICE_LABEL]
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=labels,
target_project_members=[],
)
self.assertEqual(ctx.exception.code, "helper_isolated_label_missing")
def test_helper_listed_in_target_project_is_rejected(self) -> None:
spec = executor_spec()
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=container_labels(spec),
target_project_members=[spec.container_name],
)
self.assertEqual(ctx.exception.code, "helper_listed_in_target_project")
def test_missing_provenance_label_is_rejected(self) -> None:
spec = executor_spec()
labels = container_labels(spec)
del labels[MODULE.HELPER_LABEL]
with self.assertRaises(MODULE.HelperError) as ctx:
MODULE.verify_container_isolation(
spec=spec,
baseline_labels=BASELINE_LABELS,
container_labels=labels,
target_project_members=[],
)
self.assertEqual(ctx.exception.code, "helper_provenance_label_missing")
class LauncherRunnerTest(unittest.TestCase):
def _responses(self, spec: object) -> dict[str, str]:
return {
"inspect_image_labels": json.dumps(BASELINE_LABELS),
"inspect_container_labels": json.dumps(container_labels(spec)),
"inspect_container_id": "a" * 64 + "\n",
"inspect_container_state": "true\n",
"list_target_project_members": (
f"{MODULE.TARGET_PROJECT}-api-1\n{MODULE.TARGET_PROJECT}-web-1\n"
),
}
def test_start_uses_the_exact_argv_and_returns_the_container_id(self) -> None:
spec = executor_spec()
runner = FakeRunner(self._responses(spec))
launcher = MODULE.HelperLauncher(runner)
container_id = launcher.start(spec)
self.assertEqual(container_id, "a" * 64)
self.assertEqual(
runner.argv_for("run_helper"), MODULE.build_run_argv(spec)
)
def test_verify_emits_exact_container_identity_evidence(self) -> None:
spec = control_plane_spec()
runner = FakeRunner(self._responses(spec))
evidence = MODULE.HelperLauncher(runner).verify(spec)
self.assertEqual(evidence["schema_version"], MODULE.EVIDENCE_SCHEMA)
self.assertEqual(evidence["container_name"], spec.container_name)
self.assertEqual(evidence["container_id"], "a" * 64)
self.assertTrue(evidence["container_running"])
self.assertEqual(evidence["target_project"], MODULE.TARGET_PROJECT)
self.assertNotIn(spec.container_name, evidence["target_project_members"])
def test_membership_query_filters_on_the_target_project_label(self) -> None:
spec = executor_spec()
runner = FakeRunner(self._responses(spec))
MODULE.HelperLauncher(runner).verify(spec)
argv = runner.argv_for("list_target_project_members")
self.assertIn("ps", argv)
self.assertIn(
f"label={MODULE.PROJECT_LABEL}={MODULE.TARGET_PROJECT}", argv
)
def test_stop_refuses_a_container_outside_the_isolated_project(self) -> None:
spec = executor_spec()
responses = self._responses(spec)
foreign = container_labels(spec)
foreign[MODULE.PROJECT_LABEL] = "someone-else"
responses["inspect_container_labels"] = json.dumps(foreign)
launcher = MODULE.HelperLauncher(FakeRunner(responses))
with self.assertRaises(MODULE.HelperError) as ctx:
launcher.stop(spec)
self.assertEqual(ctx.exception.code, "helper_stop_project_mismatch")
def test_stop_only_touches_the_exact_helper_name(self) -> None:
spec = executor_spec()
runner = FakeRunner(self._responses(spec))
MODULE.HelperLauncher(runner).stop(spec)
stop_argv = runner.argv_for("stop_helper")
remove_argv = runner.argv_for("remove_helper")
self.assertEqual(stop_argv[-1], spec.container_name)
self.assertEqual(remove_argv, ["docker", "rm", spec.container_name])
self.assertNotIn("--force", remove_argv)
self.assertNotIn("--volumes", remove_argv)
def test_invalid_label_json_fails_closed(self) -> None:
spec = executor_spec()
responses = self._responses(spec)
responses["inspect_image_labels"] = "not-json"
launcher = MODULE.HelperLauncher(FakeRunner(responses))
with self.assertRaises(MODULE.HelperError) as ctx:
launcher.image_labels(spec.image)
self.assertEqual(ctx.exception.code, "helper_labels_unreadable")
def test_null_image_labels_are_read_as_empty(self) -> None:
runner = FakeRunner({"inspect_image_labels": "null\n"})
launcher = MODULE.HelperLauncher(runner)
self.assertEqual(launcher.image_labels(IMAGE), {})
def test_ssh_runner_quotes_the_argv_into_one_remote_command(self) -> None:
inner = FakeRunner()
runner = MODULE.SshCommandRunner(
destination="user@host",
identity_file="C:/key",
inner=inner,
)
runner.run("probe", ["docker", "ps", "--filter", "label=a=b c"], timeout=5)
argv = inner.argv_for("probe")
self.assertEqual(argv[0], "ssh")
self.assertIn("BatchMode=yes", argv)
self.assertEqual(argv[-2], "user@host")
self.assertEqual(argv[-1], "docker ps --filter 'label=a=b c'")
if __name__ == "__main__":
unittest.main()