Arcan을 단일 사용자, 사용자 지향, 네트워크 오버레이 운영체제로 바라보며 그 구성 요소, 프로그래밍 가능한 인터페이스, 사용자 인터페이스, 호환성, 보안 설계를 개괄합니다.
Arcan이 상위 수준에서 실제로 무엇“인지”에 대한 설명을 이어갈 때가 되었습니다. 이전 글들에서는 Xorg와의 비교를 제안했습니다 ( part1, part2 ). 또 다른 가능성은 Plan9였겠지만, 이 연재의 다음(그리고 마지막) 글에도 Xorg가 더 잘 맞았습니다.
거창한 선언으로 시작해 보겠습니다:
Arcan은 단일 사용자용이며, 사용자 대면형이고, 네트워크화된 오버레이 운영체제입니다.
“단일 사용자, 사용자 대면형”이라는 말은 당신 이 핵심 관심사라는 뜻입니다. 당신 에게 제어권을 제공하는 것이 목적입니다. 수많은 동시 사용자를 “서비스”하기 위해, 가장 많은 트래픽을 라우팅하고 필터링하기 위해, 또는 지구 어디에서든 가장 빠르게 데이터를 저장하고 접근하기 위해 타협하지 않습니다.
“오버레이 운영체제”라는 말은 이것이 사용자 대면 컴포넌트들로 구축된다는 뜻입니다. Arcan은 당신이 접근할 수 있는 것이 무엇이든 받아들이고, 그 지점에서부터 확장 합니다. 이것은 Linux 커널, BSD 계열 커널, 또는 그 밖의 어떤 것의 생사에 매여 있지 않습니다. 대신 그것은 당신 이 프로그램을 개발하고 실행할 수 있는 어떤 생태계로든 옮겨 다니는 떠돌이이며, 설령 그것이 어딘가의 앱 스토어 안에 갇혀 있는 경우라 하더라도 마찬가지입니다.
그런 만큼 이것은 하드웨어 전반에 퍼져 있는 가장 지배적인 추세를 따릅니다. 그 추세란 전통적인 OS 커널을 우회해야 하는 필요악으로 취급하면서, “진짜” 운영체제는 다른 곳에 구축하는 것입니다. 이 주제에 대해 더 충분한 관점을 원한다면 USENIX ATC’21: It’s time for Operating Systems to Rediscover Hardware (Video)를 참고하세요.
이건 오래된 G👀gle식 수법입니다. 그들은 Android로 GNU/Linux에, 그리고 ChromeOS로도 같은 일을 했습니다. 이것은 선행 시대의 “embrace, extend and extinguish” 구호라기보다는 “living off the land”에 가깝습니다 — 더 큰 생태계 안에서 어디가 가장 잘 맞는지 이해하는 것입니다.
Arcan이 무엇_인가_ 라는 이 설명에서 — 그 목적 은 무엇일까요?
Arcan의 목적은 당신 주변의 모든 컴퓨팅에 대해 당신에게 자율성을 주는 것입니다.
“자율성”이란, 이러한 각 컴퓨팅 장치에서 생성되거나, 변경되거나, 그 밖의 방식으로 수정되는 상태를 당신이 이동시키고, 지우고, 교체하고, 재배치하거나, 다른 방식으로 바꿀 수 있는 능력을 뜻합니다.
돌이켜 보면 이 배경 이야기는 어렵지 않습니다. 사용자 대면 컴퓨터는 현대 생활에 어디에나 존재하며 — 우리보다 숫자가 많습니다. 당신에게는 전화기, 시계, 태블릿, “IoT” 장치, 전자책 리더기, “데스크톱” 워크스테이션, 노트북, 게임 콘솔, 그리고 각종 “스마트” 냉장고, 자동차, 계량기 등이 있습니다. 현실은, 컴퓨터를 넣을 수 있는 곳이라면, 하나든 여러 개든 반드시 거기에 계속해서 밀어 넣어진다는 것입니다.
이러한 컴퓨터가 어떻게 동작하는지의 기본 원리는 거의 다르지 않습니다. “임베디드”조차도 종종 실제 작업에 비해 과도하게 강력합니다. 반면, 당신이 분명 소유하고 있다고 여겨지는 이런 것들이 서로 협업 하게 하거나, 심지어 당신이 직접 또는 간접적으로 만든 상태를 단순히 공유 하게 하는 것조차도, 기생적인 중개자 없이는 달성하기 어렵거나 불가능한 경우가 많습니다. 이 글을 쓰는 시점에서 이 주제에 대한 최신 형태는 “cloud”의 일부입니다. 즉, 출발점(생산자)과 도착점(소비자) 사이에서 다른 누군가의 컴퓨팅을 통해 사물을 라우팅하고, 그 영향권 아래 두는 것입니다.
그 이유의 일부는 지속적 이고 의도적인 발칸화와, 조작적인 수익화 전략이 매우 오랫동안 함께 진화하며 개발 방향을 이끌도록 허용되었기 때문입니다. 암호학의 발전은 이를 더욱 굳혀 놓았습니다.
예를 하나 들겠습니다. 1990년대에는 데이터 저장소에서 디스플레이에 이르기까지 전체 수직 계층(모든 ‘레이어’)에 끊김 없는 신뢰 사슬이 있을 것이라고 생각하는 것은 터무니없었습니다. 할리우드 업계가 꾸던 가장 젖은 꿈은, 미디어 재생이 사용자에 의해 변조되거나 심지어 데이터 스트림이 관찰되는 일 없이 표시되기 전까지 완전히 보호되는 것이었습니다. 이제 그것은 전혀 터무니없는 일이 아닙니다. 그것은 기본적으로 가정되는 현실이며, 당신은 열쇠를 설정하거나 바꾸는 것조차 거의 허용되지 않습니다.
이것의 무서운 다음 진화는 당신을 센서 로 만드는 것입니다. 그리고 이는 어떤 공격자로부터 당신을 보호해 준다는 더 강력한 보안 기능 주장과 함께 판매됩니다. 편리한 부수 효과는, 실제로는 그것이 당신으로부터, 당신에 대한, 그리고 당신에 관해 수집된 데이터의 진정성을 보호하는 데 기여한다는 점입니다. 간단한 징후로는 이렇습니다. 인증 수단(비밀번호 등)이 전혀 필요 없을 때, “클라우드 속 ai”가 만든 당신의 모델은 사물을 충분히 잠가 놓은 상태이며, 당신의 편안함이 의존하는 그 맛있는 서비스에 계속 접근하고 싶다면 가장 잘 행동하는 편이 좋습니다.
이러한 전개에 설계 차원에서 어떻게 대응할 수 있는지에 대한 전체적인 상위 비전은 자매 블로그의 분기하는 데스크톱 미래를 위한 12가지 원칙에 다뤄져 있습니다 — 하지만 사회적 구현은 독자의 연습 문제로 남겨 두겠습니다.
작동하는 일반적인 아이디어의 짧은 예는 이 오래된 클립에서 볼 수 있습니다:
이것은 서로 다른 종류의 클라이언트 사이를 실시간으로 마이그레이션하는 모습을 보여 줍니다. 서로 다른 수준의 네이티브 통합을 가진 arcan 인스턴스로 이동하며, 중개자는 없습니다. 중앙에는 OpenBSD 위의 네이티브 디스플레이 서버가 있고, 왼쪽 노트북(OSX)에는 제한된 애플리케이션이 있으며, 오른쪽에는 pinephone 위의 네이티브 디스플레이 서버가 있습니다.
이 글의 핵심과 나머지 부분은 다음에 대한 개요를 제공합니다:
다음 다이어그램은 서로 다른 구성 블록들과 그것들이 어떻게 맞물리는지를 보여 줍니다.
구성 블록과 그 상호작용의 도식
SHMIF는 공유 메모리와 동기화 원시 수단(보통 세마포어)만을 필요충분한 구성 요소로 사용해 작업 간에 특권 경계를 만듭니다. 이는 애플리케이션이 shmif가 제공하는 것 외에 다른 요구가 거의 없다면, 연결 설정 이후에는 모든 네이티브 OS 호출을 제거할 수 있음을 의미합니다. 이것은 강력한 ‘최소 권한’ 구성 블록이 됩니다.
이것은 비동기 시스템 호출과 비동기 프로세스 간 통신(IPC) 둘 모두 의 역할을 하나의 인터페이스로 합쳐 수행합니다. 여기에 대한 주된 영감은 옛날의 ‘cartridges’입니다 — 사용자의 뜻에 따라 컴퓨터 전체를 꽂았다가 빼던 방식 말입니다.
현재 범위를 벗어나는 SHMIF의 레이아웃과 세부 사항에는 많은 미묘함이 있으며, 메모는 위키에서 찾을 수 있습니다. 핵심 조각은 공유된 128b 이벤트 구조체로, 고정 크기 링 버퍼 두 개를 통해 직렬화됩니다. 하나는 입력 방향이고 다른 하나는 출력 방향입니다. 나머지는 동기적으로 협상되어 전송되는 경합 중인 메타데이터이며, 이후 양측이 각각 사본을 유지하는 현재 메타데이터가 됩니다.
조건이 허락할 경우(예: unix domain socket 사용 가능성, 또는 그나마 덜 끔찍한 Win32 스타일의 DuplicateHandle 호출) 이것은 선택적으로 핸들/리소스 토큰 blob 참조로 확장됩니다. 이는 참조 방식으로 큰 버퍼를 주고받는 데 유용하며, 무엇보다 가속 그래픽스에서 선호될 수 있습니다.
주고받는 이벤트의 데이터 모델은 정적이며 lock stepped입니다. 이 모델은 단일 사용자 “데스크톱”의 요구에서 도출되었습니다. 이는 X11, Android, 그리고 QEmu식 전체 시스템 에뮬레이션 같은 특수 목적의 “컴퓨터 안의 컴퓨터” 클라이언트, 특수한 하이브리드 입력/출력 장치(예: streamdeck), 그리고 가상/증강/혼합 현실(예: safespaces)을 상대로 검증되었습니다.
shmif의 주요 부분을 요약하면 다음과 같습니다:
이 대부분은 추가적입니다 — 아주 작은 기반이 있고, 나머지는 응답하거나 폐기할 수 있는 선택적 이벤트입니다. 모든 이벤트 라우팅은 기본적으로 차단된 사용자 스크립트 가능 경로를 통과하므로, 명시적인 전달을 강제합니다. 활성 스크립트 집합(보통 당신의 ‘window manager’)이 라우팅하지 않으면 클라이언트는 어떤 것도 알 수 없습니다.
연결을 수립하거나, 새 ‘window’를 요청하거나(pull), 또는 서버 측에서 시작된 것을 받으면(push), 그 창은 변경 불가능한 타입에 묶입니다. 이 타입은 정책과 콘텐츠에 대한 힌트를 제공하여 창 관리, 라우팅, 그리고 엔진 스케줄러에 영향을 줍니다.
게임은 영화 재생과 다른 스케줄링 요구를 가지며, 아이콘은 트레이나 독 같은 다른 UI 컴포넌트에 부착될 수 있습니다. 이런 방식은 접근성 및 유사한 까다로운 예외 상황을 해결하며, 네트워크 성능 향상으로도 이어집니다.
SHMIF는 로컬에서는 최적입니다. 프로세스 간 SHMIF에 필요한 원시 수단을 충족할 수 없을 때는 A12가 있습니다. 가장 명백한 경우는 네트워크 통신입니다. 여기에는 지연이 낮은 공유 메모리가 없고, 비교적 지연이 큰 copy-in-copy-out 전송만 있습니다.
덜 명백한 다른 경우들도 있으며, 경험칙은 두 개의 서로 다른 시스템 컴포넌트가 예측 가능한 지연과 대역폭으로 공유 메모리를 통해 동기화할 수 없는 경우입니다. 예를 들어 ‘walled garden’ 생태계는 대부분의 프로세스 간 통신 형태를 금지하는 경향이 있으면서도, 네트워크 통신은 통과하도록 허용하는 경우가 많습니다.
A12는 SHMIF와 손실 없이 상호 변환되지만, 고려해야 할 추가 제약 집합이 있으며, SHMIF의 타입 모델 위에 구축되어 버퍼 동작, 혼잡 제어, 압축 매개변수에 영향을 줍니다.
설계에 부과된 제약은 많습니다. A12는 부트스트래핑에 사용할 수 있어야 하고, 적대적인 환경에서 동작해야 하며, 고립되거나 섬처럼 분리된 네트워크와 신뢰할 수 없는 시계를 가진 기계들 사이에서도 작동해야 하고, 네임스페이스가 서로 맞지 않으며 신뢰가 일시적이고 전이적일 수도 있는 상황도 감당해야 합니다. 이런 이유로 A12는 암호학에서 TLS 모델과 다릅니다. 이것은 Certificate Authorities 대신, 미리 공유된 비밀을 사용한 ‘Trust On First Use’ 관리와 정적으로 선택된 비대칭/대칭 원시 수단에 의존합니다.
브라우저가 샌드박싱에 대규모 투자를 시작하던 시기(2000년대 후반, 2010년대 초반)와 비슷한 시대에, 당시 비공개 소스 연구 프로젝트였던 Arcan 역시 보안 및 안정성에 민감한 작업을 일시적 최소 권한 프로세스 분리로 처리하는 데 집중했습니다. 이러한 작업을 수행하는 프로세스를 ‘frameservers’라고 부릅니다.
원칙적으로 ‘frameservers’는 단지 SHMIF를 사용하는 일반 애플리케이션에 추가적인 이름 규칙이 붙고, 각 실행 환경을 정리하고 설정하는 책임을 지는 체인로더(arcan_frameserver)가 있는 것뿐입니다.
실제로 ‘frameservers’는 지정된 역할(원형, archetypes)을 가집니다. 이것은 시스템의 나머지 부분이 특정 작업을 어떻게 위임하는지를 제어하고, 하나가 충돌하거나 강제로 종료될 경우 어떤 일이 일어나는지에 대해 예측 가능한 결과를 제공합니다. 또한 다양한 SHMIF 정의 이벤트에 대해 어떤 인자를 받아들이고 어떻게 행동으로 응답해야 하는지에 더 강한 계약을 부여하는 데도 사용됩니다.
여기서 다룰 가치가 있는 주요 역할은 ‘encode’, ‘decode’, 그리고 어느 정도는 ‘net’입니다.
decode 는 신뢰할 수 없는 입력 소스, 예를 들어 비디오 파일, 카메라 장치, 벡터 이미지 설명 등을 샘플링하고, 그것을 당신이 보고/또는 들을 수 있는 형태로 변환하며, 일부 출력 디스플레이에 맞게 조정합니다. 이것은 시스템 전체에 걸쳐 ‘파싱’을 단일 작업 프로세스로 통합합니다. 이러한 프로세스는 점진적으로 더 많은 권한, 예를 들어 메모리 할당이나 파일 저장소 접근 권한을 버릴 수 있는 분리된 동기 단계들을 가집니다. 보안 이야기 섹션에서 이것의 가치에 대해 조금 더 깊게 다룹니다.
encode 는 당신이 보고/또는 들을 수 있는 무언가를, 신뢰할 수 없는 출력으로 향하는 다른 표현으로 변환합니다. 예를 들면 비디오 녹화, image-to-text (OCR), 그리고 유사한 손실성 비가역 변환 형태들이 있습니다.
net 은 shmif와 a12 사이의 전환 및 핸드오버를 설정합니다. 또한 이것은 미리 수립된 신뢰 관계에 대한 네트워크 서비스 발견 역할도 합니다(“내가 신뢰하는 어떤 장치들이 사용 가능한가”, “내가 신뢰하는 새로운 장치가 사용 가능해졌는가”). 그리고 “<네임스페이스 안의 이름>으로 나가는 연결에 대한 디스크립터를 달라”와 같은 이름 리소스 중개자 역할도 합니다.
클라이언트를 이런 “일시적 단일 작업” 클라이언트와 일반 클라이언트로 나누는 것은, 전통적으로 복잡한 작업들을 위한 전용 고수준 API로 이어졌고, 동시에 다른 프로그램을 위한 대리자 로도 기능하게 했습니다.
다른 shmif 클라이언트가 “새 창을 할당하고, 이 파일이 제공된 decode frameserver에 이것을 위임한 뒤, 내 자신의 이 앵커 지점에 임베드하라”라고 아주 적은 코드 줄 수로 말할 수 있습니다. 이렇게 하면 decode frameserver가 파서/디코더/의존성 스펀지 역할을 합니다. 클라이언트는 더 단순해질 수 있고, 툴킷의 더 많은 문제를 불러들이지 않아도 됩니다.
여기서 ‘engine’은 주요 arcan 바이너리가 담당하며, node.js/electron 의미의 앱 프레임워크나 Android의 ‘Zygote’와의 유사성을 떠올리게 합니다.
이것은 두 가지 역할을 채웁니다 — 하나는 마지막 합성을 수행하고 호스트 OS와 결합하는 바깥쪽 ‘display server’ 역할입니다. 이 역할 안에서 실행되는 스크립트는 대략 ‘window manager’에 비교할 수 있지만, 당신의 모든 상호작용과 데이터에 대해 ‘firewall/filter/deep inspection’ 역할을 하므로 훨씬 더 강한 제어력을 가집니다.
다른 역할은 다이어그램에서 ‘lwa’(lightweight arcan)로 표시되어 있으며, 엔진의 별도 빌드입니다. 이 빌드는 SHMIF 클라이언트로 동작하며, 다른 ‘lwa’ 인스턴스나 디스플레이 서버로 실행 중인 가장 바깥 인스턴스에 연결할 수 있습니다. 이를 통해 같은 코드와 API가 디스플레이 서버, ‘stream processor’(참조: AWK for Multimedia), 그리고 전통적인 UI 툴킷의 ‘원시 수단’ 절반 역할을 모두 수행할 수 있습니다.
이 두 역할 모두 다이어그램에서 ‘ALT’로 표시된 API로 프로그래밍 가능하며, ‘Programmable Interfaces’와 ‘Appl’ 섹션에서 다시 다룹니다.
엔진 자체의 아키텍처와 내부 설계는 충분한 세부로 다루기에는 너무 특수합니다. 대신, 핵심적인 2D 지원 3D 게임 엔진 장르의 많은 강력한 경쟁자들과 구별되는 주요 요구사항만 나열하겠습니다.
Capability – 평범한 웹 또는 모바일 ‘app’에서부터 SF 영화식 ‘기능보다 멋’ UI에 이르기까지, 시각적·상호작용적 범위에서 애플리케이션과 사용자 인터페이스를 작성할 수 있을 정도로 충분히 진보된 그래픽과 애니메이션 지원. 네트워크 렌더링을 배제하거나 하드웨어 가속을 제공할 수 없는 장치를 배제하는 기술에 의존해서는 안 됩니다.
Abstraction – 프로그래밍 가능한 API는 aggregate/pattern (look and feel)이 아니라, primitive (기하, 텍스처링, 필터링)에 초점을 맞춰야 합니다. 변환과 애니메이션은 선언적이어야 합니다(“이것이 20 단위 시간 동안 여기로 이동하길 원한다”), (“a의 위치가 b의 위치에 상대적이길 원한다”). 그리고 스케줄링, 보간, 기타 사용자 경험 품질 매개변수는 엔진이 해결하게 해야 합니다.
Robust – 엔진은 거의 또는 전혀 다른 지원(서비스 관리자, 디스플레이/장치 관리자) 없이도 제약된 환경에서 신뢰성 있게 동작할 수 있어야 합니다. 외부 의존성을 피해야 합니다. 몇 시간이나 며칠이 아니라, 몇 달 단위로 장기간 실행될 수 있어야 합니다.
Resilient – 엔진은 자체 처리 과정의 합리적인 수준의 실패와, 변동성이 큰 하드웨어 컴포넌트(주로 GPU)의 실패로부터 복구할 수 있어야 합니다. 또한 클라이언트를 자신의 다른 인스턴스로 핸드오버/마이그레이션할 수 있어야 합니다.
Recursive – 엔진은 자기 자신의 추가 인스턴스를 장면 그래프의 다른 어떤 노드와 마찬가지로 다룰 수 있어야 합니다. 외부 소스 노드로도, 또는 서브그래프 출력 싱크 노드로도 가능합니다.
SHMIF는 IPC 시스템으로서의 사용 관점에서 이미 다뤘습니다. 그 결과로, 이것은 또한 프로그래밍 가능한 저수준 인터페이스이기도 합니다. 이를 사용하는 자세한 글은 (writing a low level arcan client)에서 볼 수 있고, 더 복잡하고 미묘한 후속 글은 (writing a tray icon handler)에서 볼 수 있습니다. QEmu UI 드라이버, arcan-wayland 브리지, Xarcan도 해킹해 볼 만한 흥미로운 참고 지점입니다.
TUI는 SHMIF 위에 구축된 추상화입니다. 일부 기능을 가리고, 특정 이벤트에 대한 기본 핸들러를 제공합니다 — 동시에 셀 그리드 또는 서식 있는 텍스트의 행 목록으로 상호 변환도 수행합니다. 아주 기본적인 위젯(readline, listview, bufferview)을 제공합니다. 스택에서의 주요 역할은 (n)curses 스타일 라이브러리를 대체하고, terminal emulation을 이제는 쉬게 하기 위한 이행 전략으로 텍스트 중심 도구를 개선하는 것입니다.
ALT는 엔진을 제어하기 위한 고수준 API(그리고 프로토콜*)입니다. 이것을 사용하는 주요 방식은 Lua 스크립트이지만, 의도는 그보다 조금 더 미묘합니다. 이 이야기의 절반은 아래 ‘Appl’을 보세요. 엔진 스크립트 인터페이스에 Lua를 선택한 이유 중 일부는 작은 크기(낮은 메모리 오버헤드와 짧은 시작 시간), 쉬운 바인딩 API, 그리고 포함된 함수 집합이 최소라는 점입니다. 이것은 일반적인 언어라기보다 C를 위한 “안전한” 함수 장식으로 취급되고 그렇게 생각됩니다.
*프로토콜 부분이란, API 문서가 이중으로 고수준 Interface DescriptionLanguage 역할도 하여, API를 프로세스 외부에서 사용하는 바인딩을 생성할 수 있게 한다는 뜻입니다 — 이를 통해 사용자의 Lua “monkey patching”과 중간 프로토콜을 둔 프로세스 분리를 모두 허용합니다. 이는 렌더 프로세스와 ALT를, 정적인 프린터 친화적 페이지가 아니라, 애니메이션과 합성 효과를 가진 애플리케이션을 위한 동적인 postscript 비슷한 것으로 만듭니다.
이것은 분리된 컴포넌트가 아니라, 코어 엔진 위에 추가된 제한과 이름 규칙의 집합입니다. 이를 이해하려면 Android와의 대략적인 비교가 다시 필요합니다.
Android App은 아주 거칠게 단순화하면, 몇 가지 해킹이 들어간 Zip 아카이브와, manifest XML 파일, 일부 Java VM 바이트코드, 선택적 리소스, 그리고 선택적 네이티브 코드입니다. 그 바이트코드는 전통적으로 Java 코드를 컴파일해서 왔지만, 여러 언어가 그것으로 컴파일될 수 있습니다. manifest는 여러 메타데이터를 담는데, 중요한 것은 앱이 어떤 시스템 리소스에 접근해야 하는지입니다.
Arcan Appl(‘l’은 한숨을 쉬거나 ‘blowing raspberries’ 하듯 발음합니다)은 다음과 같은 폴더 구조를 가집니다:
appl이 접근할 수 있는 리소스와, 파일을 생성 및 삭제할 수 있는 데이터 저장소는 여러 네임스페이스로 나뉩니다. 주요 네임스페이스는 대략 다음과 같습니다: application-local-dynamic, application-local-static, fonts, library code, shared.
Android 앱이 JNI를 통해 네이티브 코드를 로드할 수 있는 것과 비슷하게, Arcan appl도 공유 라이브러리를 동적으로 로드할 수 있습니다. Android에서는 고수준 Java/Kotlin/…를 지원하기 위해 네이티브 코드를 끌어들이는 반면, Arcan에서의 고수준 스크립팅은 네이티브 코드를 지원하기 위한 것입니다. 즉, 지루하고 오류가 발생하기 쉬운 작업을 기본적으로 메모리 안전하고 사용자가 해킹/패치할 수 있는 코드로 작성하게 합니다.
네임스페이스 자체의 매핑, 제한 또는 추가 권한, 설정 데이터베이스, 심지어 다른 frameserver 집합들까지도 각 엔진 프로세스를 시작할 때 사용한 인자들에 의해 모두 제어됩니다.
데이터베이스는 appl 자체를 위한 키 / 값 저장소 역할을 하지만, 또 다른 shmif 가능 클라이언트 중 어떤 것들이 실행 허용되어야 하는지에 대한 정책 모델 역할도 합니다(* 네이티브 코드에 대한 강제는 호스트 OS가 제공하는 제어에 따릅니다). 또한 그런 방식으로 실행된 각 클라이언트의 추적 정보를 저장하는 키 / 값 저장소 역할도 합니다.
다른 리소스 권한은 appl 자체가 직접 요청하거나 정적으로 정의하지 않으며, 그런 것들을 최종적으로 매핑하고 라우팅하는 것은 window manager입니다.
수년에 걸쳐 이 페이지에서 작성되고 소개된 여러 참조 appl이 있습니다. 이것들은 주로 ‘window manager’ 역할을 채우는 데 초점을 맞추고 있지만, 실제로는 다른 애플리케이션을 위한 구성 블록으로도 사용할 수 있습니다.
이들은 ALT API 자체의 설계를 이끄는 데 사용되었고, 어느 정도 범위의 일을 달성할 수 있는지 보여 주며, 그 자체로도 사용 가능한 도구입니다.
사용자 정의 설정이나 ‘daily drivers’를 위한 구성 블록으로 적합한 것들은 다음과 같습니다:
Console — (writing a console replacement using Arcan) 개별 클라이언트 전용의 전체 화면 작업 공간으로 동작하며, 비어 있는 작업 공간에는 기본적으로 터미널이 생성됩니다. 이는 Linux/BSD 설정의 콘솔에 해당하며 기본 빌드에 포함되어 있습니다(다만 unicode 글꼴, OSD 키보드, 터치스크린 지원, …이 있습니다).
Durden — 전통적인 데스크톱과 확립된 창 관리 방식(타일링, 스태킹/플로팅, 그리고 여러 하이브리드)의 기능 집합을 구현합니다. 이는 UI 요소와 장치 입력이 참조하는 가상 파일시스템 트리로 구성됩니다.
Safespaces — ‘가상 파일시스템’이라는 점에서는 구조적으로 Durden과 비슷하지만, 증강 현실, 혼합 현실, 가상 현실을 의도합니다.
Pipeworld — ‘데이터플로우’를 다룹니다. 프로그래밍 환경, 스프레드시트, 데스크톱의 하이브리드입니다.
업데이트를 계속 유지하지는 않고, 무언가를 보여 주기 위해 작성된 더 짧은 것들도 있습니다. 주목할 만한 예로는 Plan9와 유사한 Prio (One night in Rio – Vacation Photos from Plan9)가 있습니다.
기존 관습들과 대형 플랫폼의 네트워크/잠금 효과에는 여러 위압적인 오르막 싸움이 있습니다 — 흥미로운 애플리케이션이 없으면 투자한 사용자가 없고, 개발자가 없으면 흥미로운 애플리케이션도 없습니다.
호환성에서 가장 문제가 되는 부분은 ‘toolkit’(예: Qt와 GTK) 기반 애플리케이션들입니다. 종종 ‘이식 가능’하다고 선전되지만, 반복해서 벌어진 일은 툴킷 코드베이스 깊숙한 곳에서 찾을 수 있는 어떤 플랫폼 추상화에 묶인 조악하고 흥미롭지 않은 기능 집합으로 수렴하는 것이었습니다 — 결코 보기 좋은 일이 아닙니다.
많은 영향력 있는 프로젝트가 왜 ‘브라우저를 툴킷으로’(즉 Electron) 택했는지에는 상당한 이유가 있습니다. 대형 툴킷의 이식성 측면은 계속해서 의미를 잃을 것입니다. 잘 통합된 ‘네이티브’ 감각의 이식 가능한 소프트웨어가 장기적으로 살아남을 가능성은 거의 없다고 봅니다. 이들의 종착점은 오히려 하나의 고정된 아이디어/스타일이나 틈새에 거는 모습에 가깝습니다.
Arcan의 호환성 전략은 “양극단에 대한 강조”입니다 — 먼저 다른 애플리케이션(브라우저를 포함)을 불투명한 가상 머신으로 취급하는 극단에 초점을 맞춥니다. 호환성을 위해 레거시를 함께 가져가는 데 있어 가상화는 우리가 가진 가장 강력한 전술입니다. 이를 위해 통합의 예외 상황을 처리하고 점진적으로 그 불투명성을 깨뜨리는 여러 전술이 필요합니다. 예를 들어 side-band로 전달되는 주석을 통한 강제 분해, “guest-additions”, 가상 장치 드라이버 등이 있습니다.
전략의 두 번째 부분은 다른 극단, 즉 ‘텍스트 중심’ 애플리케이션에 집중하는 것이며, 그래서 TUI API에 그렇게 많은 작업이 들어갔습니다. 앞서 언급했듯 이것은, 컴퓨팅에 대한 절망적으로 낡고 깨진 가정에 묶여 있는 명령줄을 가진 ‘터미널’에서 벗어나는 탈출구로 필요합니다. 터미널 에뮬레이터는 오랫동안 필요할 것이며, Arcan은 기본적으로 하나를 제공하지만 — 그것은 일반적인 TUI 클라이언트입니다.
TUI는 WiFi 인증과 동적 저장소 관리 같은 악명 높게 문제 많은 시스템 제어의 프런트엔드를 구축하는 방법으로도 사용됩니다. 또한 상호작용적 전송 제어를 중심으로 데이터를 ‘감싸는’ 데도 유용합니다. UI 래핑과 합성은 appl 단계에 맡기세요.
호환성 전략의 한참 뒤 세 번째 부분은 프로토콜 브리지이며 — 현재 주요한 것은 ‘arcan-wayland’입니다. 한동안 이것이 첫 번째 전략이 될 예정이었지만, 명세가 수년 동안 한심할 정도로 불완전했고, 그 뒤에는 심각하게 혼란스러웠으며, 이제는 완전히 제정신이 아니어서 정신병원으로 보내도 될 지경입니다. 이것이 암울하게 들릴지 모르지만, 실제 구현들의 ‘품질’에 비하면 아무것도 아닙니다.
특별한 취급이 필요한 영역 하나는 보안입니다(그리고 프라이버시와 겹치는 일부도). 이 영역에서 Arcan은 특히 의견이 강합니다. 이 주제는 훨씬 더 긴 논의가 필요하며, 그 글은 대기열에 있습니다.
매우 압축해서 말하면, 주요 플랫폼들의 포괄적인 문제는 ‘보안 기능’을 계속 덧붙인다는 것입니다(그들은 당신을 위한 것이라고 말합니다). 그리고 종종 무의미한 제한이나 방해를 업데이트를 통해 점진적이고 조용하게 추가합니다 — 그것들이 실제로 무엇으로부터 당신을 보호한다고 supposed 되는지, 그리고 그에 따른 주의점이 무엇인지에 대해 당신은 알지 못한 채 말입니다.
다음 두 스크린샷은 이 문제의 입문 수준을 보여 줍니다:
sudo-sickness: “bash wants access to control Finder”
“something” wants to “use your microphone”
참고: 두 번째 것조차 대화상자가 되었다는 발상 자체가 놀랍습니다. 이런 어리석음보다 이전의 대부분 UI는 사용자가 “drag and drop”, “copy and paste” 등을 통해 자신의 주도와 상호작용만으로 데이터를 라우팅하도록 훈련해 왔습니다. 이것은 그 자체로 위험한 패턴이며, 약간만 유능한 위협 행위자라도 이 부채널을 활용하는 방법을 압니다.
이 두 가지만으로도 정말 많은 것을 뜯어볼 수 있지만, 그건 다음 기회로 미루겠습니다.
핵심 문제는 이것들이 어떤 위협 모델에는 들어맞지만, 아마도 당신의 위협 모델의 일부는 아니라는 점입니다. 현재 당신의 위협 모델이 무엇인지 실제로 표현하게 해 주는 도구, 그리고 당신의 상황에 맞게 완화책을 선택하게 해 주는 도구 — 이 둘은 사실상 존재하지 않습니다.
접근성과 비교해 보세요. 시각 저하 또는 실명을 지원하는 것은, 청각 장애와 매우 다른 요구를 가지며, 그것은 또 거동 불편과 다른 요구를 가집니다. 스크린 리더를 실행하는 것은 청력이 좋지 않은 사람에게 거의 위안이 되지 않습니다. 이런 기능들을 사용자에게 알리지도 않고 켜 버리거나, 심지어 가능한 모든 지점에서 반복적으로 물어보며 무례하게 방해하는 것은 당연히 반발을 사야 합니다.
반대편에서는, 악성코드 분석을 하는 사람과 회사의 금융 거래를 승인하는 사람, 그리고 학대적인 파트너나 고용주가 강요한 추적 앱을 써야 하는 사람은 각기 다른 필요를 가집니다. 그런데 여기서는 서로 다른 예외 상황과 실패 양상을 가진 보호 장치들이 사용자를 고려하지 않은 채 조용히 활성화됩니다.
보안 이야기는 본질적으로 동적 이고 맥락 의존적 입니다. 한 사람은 하루 동안 서로 다른 시점에 위에서 표현한 어떤 필요든 오가며 가질 수 있습니다. 좀 더 기술적으로 말하면, 집에서 편안히 있을 때는 노트북이 “뚜껑 열림 시: 자동으로 baseband radio 활성화, 알려진 액세스 포인트 검색, 연결, DHCP 설정 요청/적용”을 하고, 그와 결합된 다른 서비스가 “네트워크 연결 시: 보류 중인 업데이트 요청 및 적용” 등으로 깨어나는 것이 괜찮을 수 있습니다. 하지만 공항에 있을 때는 그것이 겉보기에는 무한히 많은 방식으로 당신을 처참하게 털리게 만들 수도 있습니다.
이것을 다시 Arcan 설계와 연결해 보겠습니다. 더 큰 이야기는 앞서 링크한 12 principles에 있으며, 그중 몇 가지는 다음과 같은 격언 으로 더 확장됩니다:
Window Manager는 현재 당신의 위협 모델을 위한 완화책 집합을 정의한다.
이것은 아마 이해하기 가장 덜 복잡한 것입니다. 더 풀어 쓰면:
요점은, Arcan의 가장 바깥 인스턴스를 제어하는 Appl 스크립트 집합(디스플레이 서버로서)을 당신의 이익을 위한 주된 제어 평면으로 만드는 것입니다. 각 클라이언트/애플리케이션을 샌드박스된 로컬 또는 샌드박스된 네트워크형일 수 있다고 생각하면, 스크립트는 임의의 소스와 임의의 싱크 사이의 라우팅/필터링/변환 규칙을 정의합니다 — 당신의 감각을 위한 방화벽입니다.
IPC는 SHMIF 외에는 없다.
메모리 안전성 취약점(대개 데이터/프로토콜 파서)은 오랫동안 시스템에 접근권을 얻는 싸고 쉬운 방법이었습니다(RCE – Remote Code Execution).
특정 완화책, 예를 들어 ASLR, NX, W^X, stack canaries 덕분에 그 비용과 난이도는 극적으로 증가했습니다. 그러나 민감한 작업의 최소 권한 분리(샌드박싱) 역시 영향을 주었습니다. 이 둘 모두 만병통치약은 아니지만, 가격과 노력을 크게 끌어올렸기 때문에, 원격 코드 실행만으로도 이미 심각한 경제적·공학적 노력이 필요할 정도입니다(public examples) — 그리고 그것은 완전한 임플란트에 들어가는 것과는 거리가 멉니다.
나쁜 프로그래밍 패턴은 완화책을 무너뜨립니다. 전체 해법을 최소 권한 중심으로 설계하지 않으면, 거의 아무것도 안전하게 샌드박싱할 수 없습니다. UNIX에서는 모든 것이 file-descriptor입니다. 따라서 file-descriptor에 대한 write()를 막아 버리면 모든 것이 깨집니다.
최소 권한 친화적이지 않은 컴포넌트들 주변에 샌드박스를 두려고 하면 무엇이 일어날까요? IPC 시스템이 생깁니다. 시스템적 관점이 없으면 그것이 아주 많이 생기게 되며, 그것들을 제대로 만드는 것은 정말 어렵습니다. Android 개발자들은 Binder(그들의 주요 IPC 시스템)에 상당한 엄밀성과 많은 노력을 들였지만, 그것은 수년 동안 직접적·간접적으로 휴대전화를 깨뜨리는 데 사용되었고 — 아마 지금도 그럴 것입니다.
실제로 보안 경계로서 테스트되거나 취급되는 IPC 시스템은 거의 없으며, 결국 공격 보안에서 ‘lateral movement’라고 부르는 일이 벌어집니다.
이것이 바로 (심호흡) 메일 첨부파일에 의해 촉발된, D-Bus (IPC)로 활성화된 인덱서 서비스 속 샌드박스되었지만 취약한 GTK 기반 이미지 파서가 heap corruption으로 RCE를 노출했고, 그 페이로드가 이어서 compositor의 Wayland (IPC) 구현 안의 수많은 Use-after-Free 중 하나를 활용했으며, 거기서 영속성을 얻기 위해 dconf (IPC)에 두 번째 단계 로더를 떨어뜨렸고, 그것이 PipeWire (IPC)가 시작한 SIP 세션 위로 PulseAudio (IPC)를 사용해 훔친 데이터를 유출하고 추가 명령을 받는 동안, 당신의 비싼 NBAD/IDS나 게으른 blue team은 알아차리지 못하는 이야기입니다 — 아마 또 하나의 음성 통화로만 보였겠지요.
현실에서는 위 시나리오가 어떤 기묘한 CTF 과제를 이기거나 여성을 감동시키는 데나 쓰일 것입니다. 실제로 벌어날 일은, 별로 흥미롭지 않은 pip 설치 Python 스크립트 의존성(또는 찬란한 크래시 수집 스크립트)이 그냥 $HOME/.ssh/id_rsa를(pastebin 비슷한 서비스로) netcat 하는 정도일 겁니다(그걸 여기저기서 다 쓰셨죠, 안 그런가요?) — 하지만 모든 것이 Rust로 다시 쓰이면 고쳐질 테니 진정하고 계속 아무것도 하지 마세요.
SHMIF의 요점은 그 하나의 IPC 시스템을 갖는 것입니다(지친 xkcd 스트립 링크는 생략합니다, 그거 아시잖아요). 모두를 끝장내기 위해서도 아니고, 또 다른 ‘programming pearls’ 판을 위해 버전 관리된 코드 생성 준비가 된 화려한 유연성을 뽐내기 위해서도 아닙니다 — 데스크톱에 필요충분한 흐름들에 대해서만 데이터 전송, 하드닝, 모니터링, 샌드박싱을 해결하기 위해서입니다.
최소 권한 파싱과 정화
모든 메모리 안전성 취약점이 똑같이 만들어지는 것은 아닙니다. 흥미로운 부분집합은 매우 작고, somehow 공격자가 제어하는 데이터에 의해 도달 가능해야 합니다. 그것은 보통 다양한 프로토콜과 문서/이미지 형식의 ‘파서’들입니다. 저를 믿지 못하겠다면 Sergey와 Meredith를 믿으세요 (Science of Insecurity).
이것은 활용될 수 있고(그리고 그래야 합니다). 가장 미친 데이터 형식(PDF)조차 파싱하는 일도 시스템 요구가 꽤 예측 가능합니다. 조금만 신경 쓰면 버퍼링 이후에는 시스템 호출 없이도 처리할 수 있고, 그 시점부터는 강력한 RCE가 있더라도 할 수 있는 일이 정말 거의 없습니다.
여기서 다시 ‘decode’ frameserver로 돌아옵니다 — 어떤 애플리케이션이든, 네이티브가 아닌 형식의 파싱을 위임할 수 있고 또 그래야 하는 알려진 바이너리입니다. 최악의 가해자들을 공격적으로 샌드박싱하는 그 바이너리 말입니다. 파싱을 조정하고 결과를 소비하는 IPC 시스템의 지원을 받으면, 이것은 분석, 수집, 퍼징 하네스까지 한 번에 갖춘 것이 됩니다. 디버깅을 개선하기 위해 display server 활용하기.
조금 더 장난기 있는 사람이라면, 이런 대리자들을 전원 재시작 시 네트워크 부팅하고 안정 상태로 리셋되는 단일 목적 장치에서 실행할 수도 있습니다. 그 장치들로 손수 제작되고 표적화된 피싱 원정을 소비하게 하고, 원격으로 디버거를 크래시 수집기로 포워딩하여, 익스플로잇 체인과 페이로드를 추출하고 역분석하는 팀에게 넘기고, 몇 개의 허니팟에 복제 주입해서 위협 행위자가 즐길 만한 상도 걸 수 있겠지요. pipeworld의 이 클립은 놀랍도록 그 시나리오 일부처럼 보이지 않나요?
정말 빠른 ‘gdb attach -p’
많은 데이터 형식이 내장형 침해 메타데이터를 담는 데 능숙해지고 있습니다. 오늘날 EXIF는 많이들 알지만, XMP에 얼마나 많은 것을 밀어 넣을 수 있는지는 덜 알려져 있습니다. 거기에는 당신의 움직임(걸음걸이)에 대한 메타데이터나, jpeg의 xmp 블록 안에 base64 인코딩된 jpeg로 숨겨진 추적 이미지 같은 별미도 들어갈 수 있습니다. 좋은 경험칙은 Adobe가 손댄 어떤 것도 사랑하는 사람들 가까이에 두지 않는 것입니다. 하나를 체계적으로 벗겨 내더라도, 새로운 무언가가 다른 곳에서 튀어나올 것이 분명합니다.
가져오기(afsrv_decode)와 (afsrv_encode)를 사람의 눈으로 관찰 가능한 표현과 스크립트 가능한 중간 모델을 사이에 둔 매우 구분된 작업으로 나누면, 한쪽에서 다른 쪽으로 넘어가는 과정에서 자연스럽게 다른 메타데이터를 인코딩하도록 정의할 수 있는 설계도 얻게 됩니다. 그리고 그렇게 전달·업로드되는 것이, 그것을 제거하는 척하지만 실제로는 미래 모델링을 위해 수집하고 신뢰하기 시작하는 어떤 “정보 기생충”(일부는 social media라고 부릅니다)에게 가는 것이라면, 아이고, 그건 신호/선택자의 가치를 떨어뜨립니다. 요점은 “이기는” 것이 아닙니다. 요점은 이런 종류의 소름 끼치는 짓의 비용을 당신의 가치보다 더 높게 만들어, 누군가가 더 정직한 생계를 시도할 유인을 갖게 하는 것입니다.
장치별 컴파트먼트.
A12와 함께, 장치에 묶인 데스크톱 컴퓨팅과 여러 네트워크 형태 사이를 오가며 전환할 수 있는 능력이 생깁니다. 이것은 개별 (‘galvanically isolated’) 장치마다 ‘한 가지 작업을 잘하기’라는 사고방식을 열어 주며, 이것은 우리가 현실적으로 감당할 수 있는 가장 강력한 형태의 위험 구획화입니다.
‘버려도 되는 장치’에서 구획화하기 가장 좋은 클라이언트는 웹 브라우저입니다. 브라우저는 OS와 하드웨어 자체의 살 속 깊숙이 발톱을 박아 넣었고, 그것의 상당 부분을 웹 애플리케이션에 노출합니다. 그 결과 그것과 독립 실행 바이너리를 다운로드하여 실행하는 것 사이의 거리는 아주 작고, 날이 갈수록 더 짧아지고 있습니다 — 우리는 somehow ELF보다도 더 나쁜 바이너리 형식을 설계하는 데 집단적으로 성공했고, 그런 일이 진정한 위업이 아니라면 무엇이겠습니까.
브라우저는 영속성과 lateral movement를 위한 충분한 기회를 제공하지만, 그 자체로도 매우 민감하고 유용한 정보를 너무 많이 집계하기 때문에, 시스템의 다른 곳을 굳이 찾을 필요조차 거의 없습니다.
이런 경우 앞서 다룬 lateral movement는 덜 흥미롭습니다. 브라우저 프로세스 안에 이미 충분한 ‘금’이 있으므로, 디스크 없는 일시적 프로세스 기생체가 거기에 앉아 자격 증명을 긁어 모으고 프록시 역할을 하는 편이 편안한 표적입니다. 요즘 아이들 말로 하자면 ‘smash and grab’이지요.
전반적으로 메모리 안전성에 대한 과도한 강조가 있어, 마치 ‘달을 가리키는 손가락’에만 시선을 고정한 나머지 하늘의 다른 영광을 놓치는 수준입니다. 메모리 손상을 악용하는 곡예적 기교를 거의 필요로 하지 않으면서도 훌륭하고 재미있는 취약점은 충분히 많고, 그중 몇 가지는 이미 언급했습니다.
아직 언급하지 않은 한 범주는 마이크로아키텍처 공격입니다 — 이것이 바로 요즘 같은 하드웨어가 점점 더 느려지는 이유 중 하나입니다. 아마 이런 공격을 영화 악당이나 어딘가 성적인 자세를 연상시키는 이름들, 예를 들어 SPECTRE와 ROWHAMMER를 통해 들어 보셨을 겁니다. CPU 마이크로코드 리비전 사이의 여러 errata 섹션만 봐도, 이 흥미로운 여정의 끝과는 아직 멀었다는 냄새가 공기 중에 남아 있습니다.
더 이상 우리 스스로를 불구로 만들기보다, ‘프로세스 분리’와 비슷한 형태의 전체 시스템 가상화가 복원력과 호환성에는 유용하지만 정당한 보안 메커니즘은 아니라고 가정합시다. 미안하지만 docker. 대신, 최악의 가해자들을 여러 장치에 나눠 놓고, 다시 말해 그것들을 정기적으로 지우고 교체하세요. 그러면 당신을 공격하는 비용이 충분히 올라가서, 렌치를 든 건달이 더 싼 해결책이 됩니다.
이쯤 되면, 한 소프트웨어의 인스턴스에서 상태를 추출(snapshot/serialize)하고 다른 장치의 같은 소프트웨어 인스턴스에 재주입(restore/deserialize)하는 일을 더 쉽게 만드는 편이 낫습니다. 결국 이것은 워크플로를 충분히 투명하고 빠르게 만들어, 새로운 일시적 브라우저 탭을 띄우는 일이 거의 즉시 일어나게 하려면 필요한 전제조건입니다.
또 다른 이점은, 축적되는 우발적 상태의 양을 줄이고, 그 상태가 무엇을 뜻하는지 들여다볼 능력을 갖게 된다는 것입니다. 이것은 당신이 무슨 이유에서인지 닫기를 거부하는 탭 안팎에 쌓여 온 더러운 습관에 대한 이야기입니다. 그걸 뒤져야 하는 가엾은 포렌식 조사관을 생각해 보세요 — 당신의 데이터를 더 많이 던져 주세요.
데이터를 제공하는 모든 것은, 노이즈를 생산하는 쪽으로 전환할 수 있어야 한다.
앞서의 마이크로폰 스크린샷이나, 더 나아가 ‘화면’ 공유를 생각해 보세요. 장치로부터 추상화되는 당신의 행동에 그것이 무슨 가치를 제공할까요? ‘당신이 공유하고 싶은 것을 제공하라’는 설계 언어는, 브라우저가 마이크 사용 권한이나 데스크톱 녹화 권한을 요청하는 것과 충분히 비슷해 보일 수 있습니다 — 하지만 사용자 상호작용을 이 다른 추상화 수준에서 작동하도록 기반을 두는 데는 꽤 많은 이점이 있습니다.
그중 일부는 순전히 실용적입니다. 예를 들어 사용자가 실수로 곤란한 자료를 포함한 데스크톱을 치우려 애쓰는 대신, 무엇을 ‘제시’할지를 생각하게 만든다는 점입니다. 소스를 명시적으로 만드는 것은, 사랑하는 사람이 보내 온 민망한 내용의 iMessage 알림 팝업이 상층 경영진과의 zoom 통화 한가운데 나타날 가능성을 훨씬 낮춰 줍니다.
미디어 스트림을 그 소스에서 분리하면(다시 말해 fsrv_decode와 fsrv_encode), 스트림이 진행되는 동안 그것을 바꾸거나 수정하는 것을 가로막는 것은 거의 없습니다. 이것은 애플리케이션이 전혀 눈치채지 못하는 사이 모든 것에 googly eyes를 붙이는 귀여운 효과에 쓸 수 있을 뿐 아니라 — 의미적으로는 유효한 스트림을 서서히 합성된 스트림으로, 또는 그 반대로 바꾸는 것도 허용합니다.
style transferring GAN이 계속 향상되고 있고, 적대적 머신러닝도 마찬가지이므로, AI는 의심할 여지 없이 그 소름 끼치는 지점을 넘어설 것입니다. 즉, AI가 만든 합성된 당신 버전이 동료와 사랑하는 사람 누구에게나 그럴듯하게 튜링 테스트를 통과할 것입니다. 이것은 또한 당신의 사이버 관계나 말실수에 대해 더 그럴듯한 부인을 가능하게 합니다. 당신은 대화를 끝내고, AI가 잠시 계속 이어 가게 할 수 있습니다. 몇 달이 지나면 당신 자신도 실제로 무슨 말을 했는지 기억하지 못할 것입니다. 그 사이에 GPT3 이야기들을 끼워 넣어 메시지 기록을 오염시키세요. 이상적으로는, 진실과 자연의 거짓말을 구분하는 비용이 사랑하는 옛 Science만큼 비싸지기를 바랍니다.
이것은 ‘공격적 프라이버시’의 구성 블록입니다 — VR만은 멀리하세요.