앱
adb 설치 없이 폰이 TV의 네트워크 ADB(5555 포트)에 직접 접속합니다. 이 글은 커밋 8681e7f(앱 버전 0.3.5)의 소스를 읽기만 하고 쓴 분석이며, 빌드·테스트·앱 실행은 하지 않았습니다.cleanyourtv-app은 폰이 직접 ADB 클라이언트가 되어 TV를 읽고(분석), 계획을 보여 주고(확인), 앱을 끄고 새 홈 화면을 설치하는(정리) Android 앱입니다. 앱은 pm disable-user --user 0 <패키지> 같은 명령을 TV 쪽 셸에 보내며, 끈 앱은 pm enable로 되살릴 수 있습니다. 코드는 Kotlin 약 55,000줄(.kt 380개) 규모입니다.
업체는 먼저 집을 둘러보고(Analyse) 무엇을 치울지 목록으로 보여 줍니다. 승인하면 원래 상태를 기록한 뒤 치우고(Clean), 새 가구(새 홈 화면)가 마음에 안 들면 알아서 되돌리는 타이머를 걸어 둡니다.
이 앱에서 목록은 Plan, 사진은 복원 지점(restore point), 타이머는 TV 안에서 도는 워치독입니다. 사용자가 보는 화면은 세 단계뿐이지만 코드의 대부분은 “실패했을 때 어떻게 되돌리는가”에 쓰입니다.
카탈로그에는 이미 cleanyourtv-deep-dive.html(Gathin23/cleanyourtv, 컴퓨터용 스크립트·런처)가 있습니다. 두 레포는 다른 저장소이고, 소스에서 확인되는 관계는 아래와 같습니다.
| 확인 항목 | 근거 파일 | 내용 |
|---|---|---|
| 코드베이스 | settings.gradle.kts | Gradle 모듈 28개로 구성된 독립 Kotlin 프로젝트(rootProject.name = "cleanyourtv-app") |
| 의존 방식 | .gitmodules | third_party/cleanyourtv 서브모듈이 Gathin23/cleanyourtv를 가리킴. 고정 커밋 9071ec8 |
| 가져다 쓰는 것 | core/trust/.../Pins.kt | 업스트림 v2.5.0 런처(패키지 com.gathin.tvhome)를 빌드해 APK로 번들. 빌드가 해시를 대조(verifyLauncherPin) |
| TV 목록 | profile-meta.json | upstreamCommit이 같은 9071ec8, 프로필 131개. 업스트림 examples/*/debloat.txt 형식을 DebloatTxt.kt가 읽음 |
| 라이선스·고지 | README.md, LICENSE | MIT. 이름과 패키지 ID(tv.cleanyour)는 업스트림 저자의 허락으로 쓴다고 README가 밝힘(허락 자체는 소스로 검증할 수 없음) |
즉 포크가 아니라 “업스트림의 런처와 TV별 목록을 입력으로 쓰는 별도 앱”입니다. 다만 이 체크아웃에는 서브모듈 내용이 비어 있어(gitlink만 존재) 업스트림 런처의 소스는 읽지 못했습니다. 포크 여부는 GitHub 메타데이터의 영역이라 여기서는 판단하지 않습니다.
adbd가 5555 포트에서 접속을 기다리고, 접속한 쪽은 셸 명령을 실행할 수 있습니다. 이 앱은 adb 실행 파일을 쓰지 않고 이 프로토콜을 Kotlin으로 직접 구현했습니다.앱을 쓰는 입장에서는 폰과 TV가 같은 Wi-Fi에 있고 TV 리모컨으로 디버깅을 켤 수 있으면 됩니다. 코드를 읽는 입장에서는 ADB 프로토콜, 명령 렌더러의 입력 검증, 되돌림 설계를 한 레포에서 볼 수 있습니다.
TrendShift 목록에서 24위로 큐에 올라온 레포입니다. 순위 말고 소스에서 확인되는 매력은 세 가지입니다.
업스트림 도구는 adb가 설치된 컴퓨터나 코딩 에이전트가 필요합니다. 이 앱은 README 기준으로 Android 9 이상 폰, 같은 Wi-Fi, TV의 네트워크 디버깅만 요구합니다. 앱의 AndroidManifest.xml에서 선언한 권한은 인터넷·네트워크 상태·Wi-Fi 멀티캐스트(TV 찾기)·포그라운드 서비스·알림·생체 인증(USE_BIOMETRIC)뿐이고, 위치·저장소·카메라·연락처는 없습니다.
TV는 잘못 건드리면 리모컨조차 안 먹는 기기입니다. 그래서 이 레포의 설계 문서와 코드는 삭제 대신 비활성화, 변경 전 상태 기록, TV 내부 워치독, 보호 패키지 목록(Denylist.kt), 위험한 TV 계열에서 홈 화면 교체 자동 생략(HomeSwitchRisk.kt)을 갖춥니다. 이는 4장에서 코드로 확인합니다.
CHANGELOG.md에는 0.1.0(2026-10-20)부터 0.3.5(2026-10-24)까지 8개 버전이 있고, git 로그의 첫 커밋은 2026-10-19, 총 419 커밋입니다. README는 “AI 코딩 에이전트와 사람의 검토로 개발했다”고 밝힙니다. 짧은 기간에 이만한 규모가 나온 것은 테스트(*Test.kt 121개)와 모듈 규칙 검사가 에이전트 작업의 안전망 역할을 했기 때문으로 읽히지만, 그 테스트들이 통과하는지는 이번 분석에서 실행해 확인하지 않았습니다.
CHANGELOG에는 특정 TV(ACER R4G 등)에서 홈 화면 교체가 부팅 문제를 일으킬 수 있어 생략 규칙을 넣었다는 기록이 있습니다. 호환 목록에 없는 TV나 처음 쓰는 버전이라면 복원 방법(복원 스크립트, TV 공장 초기화 전 안내)을 먼저 확인하고 시작하세요.
이 프로젝트에는 백엔드 서버가 없습니다. “백엔드”에 해당하는 것은 폰 앱 안의 순수 Kotlin 모듈(core/*, adb/*)이고, 인프라에 해당하는 것은 GitHub Actions와 재현 가능 빌드용 Docker 이미지입니다.
| 영역 | 기술 | 근거 |
|---|---|---|
| 언어·빌드 | Kotlin 2.4.21, Android Gradle Plugin 9.4.1, Gradle 컨벤션 플러그인(build-logic/) | gradle/libs.versions.toml |
| UI | Jetpack Compose(BOM 2026.09.00), Navigation3 1.2.0, Material3, 자체 디자인 시스템 ui/design(Outfit 폰트) | libs.versions.toml, ui/design/API.md |
| 비동기 | kotlinx.coroutines 1.11.0, StateFlow 기반 상태 머신 | core/flows/*Machine.kt |
| 직렬화 | kotlinx.serialization-json 1.11.0 (스냅샷·저널·인덱스) | JournalJson.kt, IndexJson.kt |
| 저수준 I/O | okio 3.18.2 (ADB 프레임 읽기·쓰기) | ModuleRules.kt 허용 목록 |
| 암호 | Android Keystore RSA-2048(ADB 인증), BouncyCastle 1.86(Ed25519 minisign 검증), 폰 내부 SecretBox | KeystoreAdbSigner.kt, core/trust |
TV에는 두 개의 작은 APK가 번들됩니다. 앱 자산 android/platform/src/main/assets/bundled/에 cleanyourtv-launcher.apk(약 421KB, 업스트림 홈 화면)와 cleanyourtv-curtain.apk(약 66KB, 정리 중 TV 화면에 진행 상황을 보여 주는 “커튼”)가 들어 있습니다. 커튼은 의존성이 없는 별도 TV 앱(:tv:curtain, 허용 외부 의존 0개)입니다.
| 구분 | 기술 |
|---|---|
| 테스트 | JUnit4, Robolectric 4.17, kotest 6.2.6, turbine, Roborazzi 1.76(스크린샷 골든 — 저장소의 PNG 약 1,100개 대부분이 이 쪽), 가짜 adbd 서버(testing/fake-adbd), 에뮬레이터 E2E(e2e/ S00~S09 시나리오) |
| CI | pr.yml(테스트·린트·골든·R8 릴리스 빌드·APK 8MB 크기 게이트), release.yml(두 번 빌드해 바이트 비교), fdroid.yml, nightly.yml, pages.yml |
| 배포 | GitHub 릴리스 APK(full 플레이버), F-Droid 레시피(docs/fdroid/), Google Play용 AAB(play 플레이버) |
| 재현 빌드 | docker/repro/Dockerfile, scripts/verify-release.sh(서명된 APK = 공개 소스 빌드 + 서명인지 apksigcopier로 대조) |
같은 코드에서 두 가지 스토어 빌드가 나옵니다. full은 GitHub에서 업데이트를 확인하고 추가 모듈을 내려받지만, play는 업데이트 확인·외부 다운로드가 없고 네트워크 보안 설정에 GitHub 호스트가 없습니다(app/src/full, app/src/play의 Store.kt와 network_security_config.xml).
ModuleRules.kt)모듈은 계층 번호를 갖고, 자기보다 낮은 번호의 모듈에만 의존할 수 있습니다. 규칙은 데이터로 코드에 적혀 있고, checkModuleGraph Gradle 작업이 실제 빌드 그래프를 검사합니다. 외부 라이브러리도 모듈별 허용 목록이 있습니다.
core/model/.../Ops.kt의 sealed interface Op는 TV에 할 수 있는 일의 어휘 전부입니다. Disable, Enable, DefaultState, Install, SettingsPut, SetHome, ArmHomeWatchdog, PushAsset, Reboot, DebugOff, CurtainCue 등 20여 개가 있고, 임의의 셸 문자열을 담는 항목은 없습니다. ShellRenderer가 이 Op를 “상수 단어 + 검증된 토큰”으로만 조립해 정확히 한 줄로 만들고, 쓰기 Op마다 확인용 읽기(verify)를 붙입니다.
// core/ops/ShellRenderer.kt (발췌)
is Op.Disable -> Rendered(op, lines(line(PM, "disable-user", USER, "0", writePkg(op.pkg))), null,
Privilege.PROFILE_STEP, Op.Read(ProbeKind.DISABLED), T_PM, CAP_DEFAULT)
is Op.Enable -> Rendered(op, lines(line(PM, "enable", USER, "0", pkg(op.pkg))), null,
Privilege.PROFILE_STEP, Op.Read(ProbeKind.DISABLED), T_PM, CAP_DEFAULT)
패키지 이름·클래스·실행 토큰은 정규식(PACKAGE_RE, CLASS_RE, RUN_TOKEN_RE)으로 만든 값 클래스이고, 렌더러는 한 번 더 검증합니다. 따라서 악의적이거나 깨진 패키지 이름이 셸 메타문자를 끌고 들어오기 어렵게 되어 있습니다.
adb/session/.../Support.kt의 PmGuard는 렌더러와 별개로, 세션이 TV에 보내기 직전에 줄을 다시 검사합니다. pm 쓰기 명령이 있는 줄에 ; | & 같은 연결 문자가 섞여 있으면 거부하고, 예외는 워치독 템플릿과 정규식 WATCHDOG_RE가 줄 전체와 일치할 때뿐입니다. 한 군데 버그가 있어도 다른 층이 막도록 한 구조입니다.
홈 화면을 바꾸는 순간 폰과의 연결이 끊기거나 새 홈이 죽으면 TV를 쓸 수 없습니다. Watchdog.kt는 TV 셸에서 다음 본문을 백그라운드로 실행합니다.
sleep SECONDS & wait; [ -f /data/local/tmp/cleanyourtv/home-ok-TOKEN ] && exit 0;
pm enable --user 0 STOCKPKG; cmd package set-home-activity --user 0 STOCKCOMP;
am start -W -a android.intent.action.MAIN -c android.intent.category.HOME
폰이 새 홈 화면을 확인하면 home-ok-TOKEN 파일을 만들어 타이머를 무력화하고, 확인이 없으면 TV가 스스로 기본 런처를 되살립니다. 기본 대기는 DefaultPlanner.WATCHDOG_SECONDS = 150초이고(허용 범위 30~300초), 폰 쪽 확인 창은 confirmWindowMs = 90_000입니다. 소스 주석에는 sleep & wait로 쓰는 이유(TV의 sh가 포그라운드 sleep 중에는 SIGTERM을 미뤄 워치독이 안 죽던 Mi TV 실기기 문제)가 적혀 있습니다.
Engine.kt의 JournalRecord(PlanStart, Begin, Done, Failed, Section, Rollback, Paused, End)는 CRC32와 함께 파일에 먼저 기록되고 force된 뒤 다음 단계로 갑니다. 구간 상태는 ARMED → CONFIRMED | ROLLED_BACK이고, 연결이 끊겨 InDoubt가 되거나 구간이 열린 채 남으면, DefaultEngine의 복구 코드가 저널에서 가장 최근 ARMED 기록을 찾아 구간을 이어 가거나 롤백합니다(이 복구 경로를 실제로 돌려 보지는 않았습니다). 상태는 Idle, Running, AwaitingConfirm, Paused, RollingBack, InDoubt, Finished 일곱 가지입니다.
HomeSwitchRisk.kt는 “정리만 하고 홈 화면은 건드리지 않는” 조건을 데이터로 가집니다.
| 위험 분류 | 의미(소스 주석 요지) |
|---|---|
WIZARD_WIFI / WIZARD_WIFI_UNKNOWN | 초기 설정 마법사가 저장한 Wi-Fi를 지우게 되거나 누가 저장했는지 모를 때. 마법사가 켜져 있으면 홈 교체 시 시스템이 크래시 루프에 빠질 수 있음 |
WIZARD_STAYS_ON | 어떤 프로필도 모르는 TV. 마법사는 켜진 채라 같은 위험 |
CASTING | Google 자체 기기(sabrina, boreal, kirkwood). 홈 교체에 마법사를 꺼야 하는데 그러면 캐스트가 재시작마다 끊김 |
BOOT_LOOP | MOKA 계열(R3_GTV, R10G, R4_GTV 등). 재시작마다 마법사가 다시 켜지고, 기본 홈이 꺼져 있으면 부팅 실패 사례 |
나머지 안전 구조는 Denylist.kt입니다. 프레임워크(android), 시스템 UI, 설정, 패키지 설치 관리자, 권한 컨트롤러, Play 서비스·Play 스토어, 블루투스(리모컨 페어링), 네트워크 스택·Wi-Fi 계열 접두사가 코드 상수로 박혀 있어 어떤 프로필도 이를 덮어쓸 수 없습니다. 상시 실행(persistent) 패키지, TV 입력(HDMI·튜너) 제공 패키지, 현재 키보드·화면보호기도 실시간 사실로 보호합니다.
선택 모듈(예: 번들 인덱스의 “Retro” NES 게임 앱)은 서명된 목록으로만 제공됩니다. MinisignIndexVerifier의 검증 순서는 고정입니다.
keys.json이 앱에 컴파일된 루트 공개키(TrustRoots.INDEX_ROOT_PUBLIC_KEY)의 minisign 서명을 갖는지, 번들된 것보다 오래된 파일이 아닌지 확인index.json 서명 키가 폐기(revoked)·미등록·서명 불일치가 아닌지 확인ROLLBACK 거부, 만료일이 지났으면 EXPIRED 거부네트워크 접속 허용 호스트는 DownloadHosts.kt에 github.com, api.github.com, objects.githubusercontent.com, release-assets.githubusercontent.com 네 개뿐이고, https·기본 포트·사용자 정보 없음·정확한 호스트 일치를 리다이렉트 매 단계마다 검사합니다.
| 순서 | 파일 | 이유 |
|---|---|---|
| 1 | README.md, CHANGELOG.md | 사용자가 보는 약속과 최근 변경(어떤 TV에서 무엇이 문제였나) |
| 2 | core/model/.../Ops.kt | 할 수 있는 일의 전체 어휘 |
| 3 | core/ops/.../ShellRenderer.kt, Watchdog.kt, Denylist.kt | Op가 어떤 한 줄이 되는지, 무엇이 금지인지 |
| 4 | core/planner/.../DefaultPlanner.kt, HomeSwitchRisk.kt | 계획을 만드는 규칙과 홈 교체 생략 조건 |
| 5 | core/engine/.../DefaultEngine.kt | 저널, 가드 구간, 롤백(약 1,000줄) |
| 6 | adb/wire/.../Protocol.kt, Connection.kt | ADB 프레임·인증 핸드셰이크 |
코드 주석에는 “DECISIONS-W5.md, K2”처럼 docs/history/의 문서 이름이 자주 나옵니다. README에 따르면 PRODUCT-SPEC·ARCHITECTURE·SECURITY 같은 원 설계 문서는 코드보다 먼저 쓰였고 이 저장소에는 없으니, 의도를 확인하려면 docs/history/DECISIONS-W*.md와 reviews/를 보는 것이 가장 빠릅니다.
adb/wire/Protocol.kt는 CNXN, AUTH, OPEN, WRTE, OKAY, CLSE, STLS 명령 상수를 정의합니다. dadb에서 가져온 코드지만 파일 머리말에 변경점이 적혀 있습니다: 명령마다 읽을 길이의 상한을 두고(제어 메시지 256KiB, 데이터 프레임 1MiB), 체크섬과 매직 워드를 검증하고, 알 수 없는 명령은 거부합니다. HostileFrameFuzzTest는 임의로 깨뜨린 프레임을 읽는 쪽이 견디는지 검사합니다.
배울 것: 길이 필드를 믿지 않는 파서, 바이너리 프로토콜의 퍼징 테스트, 라이선스 파생 고지(Apache-2.0 NOTICE).
KeystoreAdbSigner는 RSA-2048 키를 StrongBox, 안 되면 TEE, 그것도 안 되면 소프트웨어 키(SecretBox로 봉인) 순서로 만들고, 각 경로는 자체 점검을 통과한 뒤에만 사용합니다. 서명은 20바이트 토큰에 대한 DigestInfo(SHA-1) PKCS#1 v1.5 한 가지로 제한해 “아무 데이터나 서명해 주는 오라클”이 되지 않게 했습니다.
값 클래스(정규식) → 렌더러 재검증 → 세션 PmGuard. 같은 규칙을 서로 다른 층에서 독립적으로 확인하는 방식은 셸을 다루는 어떤 프로젝트에도 옮겨 쓸 수 있습니다.
core/ops/src/test/resources/golden/에는 Op별 기대 출력이 01-disable.txt부터 59-cue-finale.txt까지 59개와 planhash.txt로 저장되어 있습니다. 렌더러가 바뀌면 TV로 가는 한 줄이 바뀌므로 테스트가 바로 깨집니다.
testing/fake-adbd는 실제 ADB 프로토콜로 응답하는 가짜 TV 서버이고 Persona로 기기별 프로퍼티·패키지 목록을 바꿉니다. fixtures/conformance/mitv-axso0-2026-10-18 같은 실제 TV 캡처가 가짜 TV의 기준이 됩니다. 캡처에서 시리얼 등을 지우는지는 scripts/check-redaction.py가 CI에서 검사합니다.
CI는 서명 키를 갖지 않고, 컨테이너에서 같은 커밋을 두 번 빌드해 바이트 단위로 비교한 서명 전 APK를 올립니다. 관리자가 로컬에서 서명한 APK는 verify-release.sh가 “서명 전 빌드 + 서명뿐”인지 확인합니다. README는 릴리스 인증서 SHA-256(eaf7af38…cd65)을 공개합니다.
이 레포의 가장 값진 학습 포인트는 기능이 아니라, 모든 위험한 동작에 “입력 제한, 사전 기록, 확인 읽기, 복구 경로”를 한 세트로 붙이는 습관입니다.
| 대상 | 요구 | 근거 |
|---|---|---|
| 폰 | Android 9 이상(MIN_SDK = 28), 화면 잠금 설정, TV와 같은 Wi-Fi | README, Support.kt |
| TV | 네트워크 ADB(5555) 켜기. 리모컨으로 켜는 방법을 앱이 안내 | README |
| 페어링 코드만 있는 TV | 컴퓨터에서 한 번 페어링해야 함(앱이 명령 3개를 보여 줌) | README “Network debugging” |
| 정리만 하고 홈 교체는 안 하는 TV | Fire TV, TCL 중국 빌드, Android 7, 알 수 없는 벤더 홈, Chromecast with Google TV·Google TV Streamer 등 | README “Supported TVs”, HomeSwitchRisk.kt |
| 빌드 | JDK 21, Android SDK(compile 37, build tools 36), Linux 또는 macOS, 서브모듈 초기화 | CONTRIBUTING.md |
| APK 크기 | 릴리스 APK/AAB는 8MB 크기 게이트 | CONTRIBUTING.md, ApkSizeCheckTask.kt |
| GPU·서버·API 키 | 필요 없음. 계정·분석·광고·크래시 리포트 서비스도 없음(README 개인정보 절) | README, PRIVACY.md |
지원 범위는 README가 “131개 모델, 46개 브랜드”로 밝히고, 소스에서는 profile-meta.json의 프로필 수 131이 확인됩니다. 모델이 목록에 없어도 브랜드 공통분모(BrandSureSet)만 끄는 보수적 정리를 합니다.
Watchdog.kt의 BODY에서 SECONDS, STOCKPKG, STOCKCOMP, TOKEN 네 자리표시자를 직접 채워 보고, core/ops/src/test/resources/golden/16-arm-home-watchdog-a.txt와 같은지 비교하세요. 이어서 PmGuard.WATCHDOG_RE가 이 줄만 통과시키는 이유를 정규식 조각별로 설명해 봅니다.
JDK 21과 Android SDK를 준비한 뒤(local.properties에 sdk.dir) 서브모듈과 함께 클론합니다. 저는 이 명령들을 실행하지 않았으므로, 첫 시도는 가장 작은 모듈부터 하는 것이 안전합니다.
git clone --recurse-submodules https://github.com/udhaykumarbala/cleanyourtv-app.git
cd cleanyourtv-app
echo "sdk.dir=$ANDROID_HOME" > local.properties
./gradlew :core:ops:test --console=plain
통과하면 GoldenTest의 기대 파일 하나를 일부러 고쳐 어떤 실패 메시지가 나오는지 확인한 뒤 되돌려 보세요.
가상의 시스템 패키지 하나를 정해 WatchdogAndProtectedTest.kt 스타일로 “보호 목록에 있으면 Op.Disable 렌더링이 거부된다”는 테스트를 먼저 쓰고, Denylist.exact에 한 줄을 넣어 통과시킵니다. 실제 PR로 보낼 목적이 아니라면 로컬 브랜치에서만 하세요. 보호 목록은 안전장치라 CONTRIBUTING.md의 검토 규칙을 먼저 읽어야 합니다.
core/spec/candidates/sony-bravia-vh1/debloat.txt는 “DRAFT, NOT TESTED” 머리말과 “tracking” 같은 그룹 주석, 패키지 줄, # 메모로 이루어져 있습니다. DebloatTxt.kt의 정규식(PACKAGE_LINE)과 그룹 규칙을 읽고, 직접 만든 3줄짜리 파일로 파서 테스트를 써 보세요. 후보 목록은 릴리스 빌드에 포함되지 않고 테스트 빌드의 특정 옵션으로만 제공된다고 CHANGELOG 0.3.5가 밝힙니다.
e2e/src/emulatorTest/.../S03DebloatAndUndo.kt와 docker/emulator/Dockerfile을 읽고, Android TV 에뮬레이터를 하나 띄워 앱의 Setup 흐름을 처음부터 끝까지 본 뒤 Manage 화면에서 “하나만 되돌리기”와 “전부 되돌리기”를 비교합니다. 실제 TV는 이 과제의 대상이 아닙니다.
| 주차 | 주제 | 할 일 |
|---|---|---|
| 1주 | ADB와 Android 패키지 관리자 | adb shell pm list packages -d 같은 읽기 명령, pm disable-user와 uninstall의 차이를 공식 문서로 정리. 에뮬레이터에서만 실습 |
| 2주 | ADB 프로토콜 | Protocol.kt, Connection.kt, AdbAuth.kt를 읽고 CNXN→AUTH→OPEN 흐름을 그림으로 그리기 |
| 3주 | Kotlin 코루틴·상태 머신 | core/flows의 SetupMachine을 읽고 StateFlow로 화면 상태를 설계하는 방법 정리 |
| 4주 | 안전한 명령 실행 설계 | 렌더러·가드·골든 테스트·저널을 묶어 “되돌릴 수 있는 변경 엔진” 최소판(Op 3개)을 직접 구현 |
| 5주 | 서명과 공급망 | minisign 검증 흐름, 재현 빌드, apksigner 확인. 이 레포의 verify-release.sh를 읽고 각 단계가 막는 공격을 적기 |
| 6주 | Compose UI 테스트 | Robolectric+Roborazzi 골든 테스트가 어떻게 화면 변화를 잡는지 feature/setup/src/test에서 확인 |
| 키워드 | 뜻 |
|---|---|
| Op | TV에 할 수 있는 일의 한 단위(sealed interface). 셸 문자열이 아니라 타입 |
| Plan / Step | Op들을 순서대로 묶은 실행 계획과 그 한 칸. hash로 승인한 계획과 실행할 계획이 같음을 확인 |
| Prior | 변경 전 상태(켜짐/꺼짐, 설정값, 홈 등). 되돌릴 때 쓰는 근거 |
| Snapshot | probe가 읽어 온 TV 상태. 계획은 특정 snapshot의 해시를 기반으로 만들어짐 |
| Probe | TV 상태를 읽기 전용 명령으로 수집하는 단계 |
| Profile | 특정 TV 모델에 맞춘 “끌 앱/끄면 안 되는 앱/특이사항” 목록. 업스트림 debloat.txt에서 변환 |
| BrandSureSet | 모델 프로필이 없을 때 같은 브랜드 목록이 모두 동의하는 항목만 쓰는 보수적 목록 |
| Denylist | 어떤 프로필도 덮어쓸 수 없는 보호 패키지 목록 |
| GuardedSection | 홈 화면 교체처럼 실패 시 롤백이 필요한 구간. 워치독 무장→교체→확인으로 구성 |
| Watchdog | TV 셸에서 도는 데드맨 타이머. 확인 파일이 없으면 기본 런처를 복구 |
| Curtain | 정리 중 TV 화면에 진행 상황을 보여 주는 별도 TV 앱 |
| Restore point | 변경 전 상태 기록. 폰 안에 보관하고 원하면 restore.sh/restore.ps1로 내보냄 |
| Module / Module index | 선택 설치 앱(예: Retro)과 그 목록. 목록은 minisign으로 서명됨 |
| Minisign | Ed25519 기반 서명 도구·형식. 인덱스와 키 파일 검증에 사용 |
| Flavour (full/play) | 같은 코드의 스토어별 빌드 변형. play는 GitHub 접속·업데이트 확인이 없음 |
8681e7f92f057efc9530e95809491ea06bf175fd)v0.3.6-beta1이 있음