CC Safety Net
코딩 에이전트 CLI 안전층으로 실행 전 파괴적 git·파일시스템 명령과 비밀 파일 접근을 차단
Rinse는 Mac App Store에 있으며 Safari에서 광고와 추적기를 차단하고 YouTube 광고를 제거합니다
Rinse is on the Mac App Store. It removes ads from YouTube and blocks ads and trackers on every site in Safari on Mac. No account, no VPN, nothing collected.
보안JavaScript · SwiftGitHub @nikolaikushner온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Ad-free YouTube and a cleaner web in Safari. On the Mac App Store now; iPhone and iPad are still in review. YouTube ad removal with background playback, plus content blockers from EasyList, EasyPrivacy and RU AdList.
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Rinse 1.0 is on the Mac App Store for macOS 15 and later. iPhone and iPad are still in review; the same purchase will cover them.
Rinse is a Safari extension app in the spirit of Vinegar, with a site-wide ad blocker built in.
youtube.com. Nothing is collected or sent anywhere.| Extension | What it does | Access |
|---|---|---|
| Rinse for YouTube | Strips ad payloads from the player, skips stitched ads, optional background playback | youtube.com only |
| Rinse Ads | Blocks ad networks (EasyList + RU AdList network rules, 75.7k) | none |
| Rinse Privacy | Blocks trackers and analytics (EasyPrivacy, 56.3k) | none |
| Rinse Page Cleaner | Hides leftover ad slots, banners and brandings (element hiding, 11k) | none |
The app shows at a glance whether everything is on, walks you through enabling the extensions step by step, and holds the settings: background playback and the sites where Rinse is off. It's in English and Russian.
flowchart LR
subgraph YouTube["Rinse for YouTube (Web Extension)"]
BG["background.js<br/>scripting.executeScript<br/>world: MAIN"] --> PAGE["page.js<br/>YouTube's JS world"]
CS["content.js<br/>isolated world"]
end
PAGE -- "prunes adPlacements / adSlots / playerAds<br/>from ytInitialPlayerResponse, fetch, XHR" --> P[YouTube player]
PAGE -- "hides visibility & focus changes" --> P
CS -- "ad-showing? mute · 16× · press Skip" --> P
CS -- "iOS paused on background? play()" --> P
subgraph Blockers["Content Blockers"]
L["EasyList · EasyPrivacy · RU AdList · filters/rinse.txt"] --> C["tools/filters/compile.py"] --> J["blockerList.json ×3"]
end
J --> S[Safari applies rules natively]
YouTube. A custom <video> player like the original Vinegar is no longer possible: as of 2026 YouTube serves no HLS, and direct stream URLs return 403 after about 12–16 MB without a PO token (spike report). So Rinse keeps YouTube's own player and takes the ads out of it:
page.js runs in YouTube's own JS world and deletes the ad fields from player responses before the player reads them. It's injected through scripting.executeScript, because YouTube's CSP blocks a plain <script src>.content.js hides it with rinse.css and mutes it. A separate ad video jumps straight to its end; a stitched one plays at 16×. Then the ad's own Skip button gets pressed, found by its label inside the ad UI.
After a removed or skipped ad, YouTube holds the video back server-side for about the ad's length. That can't be avoided from the page, so a small "Ad skipped · your video starts in a moment" note is shown instead of a frozen-looking player.
YouTube's "Ad blockers are not allowed" popup pauses the video and dims the page. page.js prunes it from the player response; if it still opens, rinse.css hides it and content.js resumes the video. The dimming backdrop is removed by content.js whenever no dialog is displayed over it: a dialog hidden with display: none never finishes its close transition, so YouTube would leave the backdrop over the page, blocking every click.content.js caches it in storage.local and sets <html data-rinse-background> for page.js. When it's on, content.js swallows visibilitychange, pagehide, blur and freeze before YouTube sees them, page.js reports the page as visible, and if iOS still pauses the video while going to the background, it's resumed.Everything else. tools/filters/compile.py converts Adblock Plus syntax into Safari's content-blocker JSON:
@@ exceptions;#@# exceptions;site.* TLD wildcards, turned into page URL filters.What Safari can't express (regex rules, $redirect, scriptlets, procedural selectors) is dropped and counted. The output is verified with WebKit's own compiler, and each CSS selector is checked by WebKit, because a single invalid one would silently disable the 250 selectors batched with it.
nikolaikushner.github.io/rinse · Privacy Policy · Support (also in Russian). The pages live in site/ and are published by .github/workflows/pages.yml on every push to main that changes them.
Requirements: Xcode 27+, XcodeGen (brew install xcodegen), and an Apple Developer team. The team ID is set in project.yml.
scripts/run.sh mac # build and open the Mac app
scripts/run.sh ios # build and install on a connected, unlocked iPhone or iPad
Or run xcodegen generate && open Rinse.xcodeproj and use the Rinse_iOS / Rinse_macOS schemes. Rinse.xcodeproj is generated and not committed.
scripts/release.sh # archive iOS + macOS, sign for App Store Connect, upload
scripts/release.sh ios # or one platform
Bump CURRENT_PROJECT_VERSION in project.yml before each upload: a build number can be used only once.
Signing is automatic with the team in project.yml and the Apple ID signed in to Xcode.
Open the Rinse app once after installing or updating, so Safari reloads the rules.
tools/youtube/try-youtube.swift plays a real YouTube video in a WKWebView with Rinse's scripts and prints a per-second timeline: is an ad showing, is it hidden, does the video advance. tools/youtube/check.sh runs it several times on videos known to show ads; --fake-popup N opens a stand-in for the ad blocker popup at second N, to check that Rinse hides it and resumes the video (--orphan: the dialog is already hidden and goes away, the backdrop stays; --plain: an ordinary dialog, which must stay). Run it before a release; with full Rinse you want ad=0s, and any stall should be covered by the note (silentStall ≤ 1s).
swift tools/youtube/try-youtube.swift oufxWa84Fxk # full Rinse
swift tools/youtube/try-youtube.swift oufxWa84Fxk --no-rinse # baseline
tools/youtube/check.sh 3
scripts/update-filters.sh # download + compile
python3 -m unittest discover -s tools/filters # compiler tests
tools/tests/run-allowlist-tests.sh # allowlist, incl. WebKit
swift tools/filters/validate.swift Extensions/Blockers/*/blockerList.json # WebKit accepts the rules
If WebKit starts rejecting selectors, regenerate the reject list:
python3 tools/filters/compile.py --dump-selectors /tmp/s.txt
swift tools/filters/check-selectors.swift /tmp/s.txt > filters/invalid-selectors.txt
To see Rinse's state on a YouTube page, run localStorage.setItem("rinse-debug", "1") in the page console and reload. A status badge then shows whether page.js ran and how many ad fields were pruned, ads skipped and background resumes.
| Path | What |
|---|---|
App/ | SwiftUI container app: extension status, rule reload, how to enable |
Shared/ | Code shared by the app targets |
Extensions/YouTube/ | Safari Web Extension: content.js, page.js, background.js |
Extensions/Blockers/{Ads,Privacy,Cosmetic}/ | Content blockers; blockerList.json is generated, don't edit it by hand |
filters/ | sources.json (which lists go into which blocker), rinse.txt (our own rules), invalid-selectors.txt |
tools/filters/ | Filter compiler, its tests, and WebKit-based validators |
scripts/ | run.sh, update-filters.sh, icon generator |
site/ | Website for GitHub Pages: landing, privacy policy, support (EN + RU) |
Config/PrivacyInfo.xcprivacy | Privacy manifest bundled in the app and every extension |
docs/appstore/ | App Store listing (EN + RU), review notes, screenshots and their gallery (gallery.html); built by tools/appstore/make-screenshots.sh and gallery.py |
plans/, docs/spikes/ | Implementation plan and research notes |
spikes/ | Throwaway experiments and their tests, kept for reference |
Rinse 1.0 is on the Mac App Store. It has been tested on an iPhone 14 Plus and a Mac, and not yet on iPad. iPhone and iPad are not in the store yet.
page.js and content.js fall back to YouTube's normal player rather than a black screen.See plans/rinse.md for the roadmap: an element picker ("hide this"), a per-site allowlist, Shorts, embedded players, and the iPhone and iPad release.
Copyright © 2026 Mikalai Kushner.
blockerList.json files stay under those licenses. See LICENSES.md.Inspired by Vinegar by And a Dinosaur. Rinse is not affiliated with YouTube or Google.
Rinse 1.0 уже в Mac App Store для macOS 15 и новее. iPhone и iPad ещё на проверке.
Rinse — набор расширений Safari для iPhone, iPad и Mac.
youtube.com.Сборка: scripts/run.sh mac или scripts/run.sh ios. Затем включите все четыре расширения в настройках Safari и один раз откройте приложение Rinse.
Лицензия. Та же покупка покроет iPhone и iPad, когда их версии одобрят. Продавать Rinse и выкладывать его в магазин приложений может только автор. Себе собрать для личного пользования можно. Название «Rinse» и иконка в эту лицензию не входят. Списки EasyList, EasyPrivacy и RU AdList чужие, и их лицензии по-прежнему разрешают другим ими пользоваться.