dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
클라우드플레어 워커에서 사용자별 클로드·에이전트 스킬을 발급하고 다운로드 시 API 비밀키가 자동 삽입됩니다
Mint per-user, revocable Claude/agent skills from a Cloudflare Worker — the API secret bakes itself into the skill at download time. HMAC-derived keys, never stored; one-integer revocation; no app-lev
보안TypeScript · PythonGitHub @htlin222온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Mint per-user, revocable Claude/agent skills from a Cloudflare Worker — the API secret bakes itself into the skill at download time. HMAC-derived keys, never stored; one-integer revocation; no app-level auth.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Mint per-user, revocable Claude skills from your own Cloudflare Worker — the secret bakes itself into the skill at download time.
Live, against stash-demo.hsieh-ting-lin.workers.dev: the credential is baked in (never typed), the CLI just works, then revoking it server-side kills the already-downloaded skill on the spot.README 전문 보기 ↗Mint per-user, revocable Claude skills from your own Cloudflare Worker — the secret bakes itself into the skill at download time.
Live, against stash-demo.hsieh-ting-lin.workers.dev: the credential is baked in (never typed), the CLI just works, then revoking it server-side kills the already-downloaded skill on the spot.
A tiny, self-contained reference implementation of a paradigm: instead of
pasting an API key into every agent and every machine, your server mints a
personalized Agent Skill
on download — a .skill bundle with a .env already baked in. The agent
unzips it and immediately knows your infra, authenticated, with a credential
that is scoped to one person, derived not stored, and revocable with a single
integer.
Runs free on Cloudflare Workers + KV + D1. No app-level auth code — Cloudflare Access handles identity; this repo handles minting.
Every time you start a new project, your agent doesn't know your private infra. So you do one of two bad things:
Static secrets don't fit a world where agents are the API consumers. You want to hand each agent (each person, each laptop) its own credential, know who is calling, and be able to kill exactly one of them without rotating everyone.
The skill carries its own secret, and the server mints it per user.
┌─────────────┐ Access login ┌──────────────────────────┐
│ browser │ ────────────────▶ │ Worker: GET /api/me/skill│
│ (console) │ │ bakes .env { base,key, │
└─────────────┘ ◀── stash.skill ─│ email } + zips skill/ │
(zip w/ .env) └──────────────────────────┘
│ unzip into ~/.claude/skills/
▼
┌─────────────┐ Bearer key + ┌──────────────────────────┐
│ any agent │ X-User-Email │ Worker: /api/kv/* │
│ (stash CLI)│ ────────────────▶ │ re-derives key, verifies │
└─────────────┘ ◀── data ────────│ (the ONLY gate, public) │
└──────────────────────────┘
The key is never stored anywhere:
key = "stashk_" + base64url( HMAC-SHA256( SERVER_SECRET, `${email}:${version}` ) )
SERVER_SECRET is one global Worker secret.version is a per-user integer in D1 — the only per-user state.(secret, email, version) and constant-time compares. Nothing to look up, nothing to leak.| Static API key | skill-with-secret |
|---|---|
| Stored in DB / env → leaks if either leaks | Derived, never stored. Leak the whole DB → zero usable keys |
| One key for everyone | One key per person, bound to their email by HMAC |
| Revoke = rotate the shared key = break everyone | Revoke one user by bumping their integer version |
| Agent must be told the URL + key by hand | Baked into the skill at download — agent just works |
| Can't tell who's calling | Every call carries a verified email |
| Anyone can probe "is this email a member?" | Anti-enumeration: unknown email and bad key are indistinguishable, with uniform timing |
This is the right shape for the agent era: a credential that is minted, self-installing, attributable, and individually revocable — with the auth boundary kept at the edge (Cloudflare Access), not reimplemented in your app.
A public, no-login demo running the exact same Worker with DEMO_MODE=1, which
swaps Cloudflare Access for a throwaway per-browser identity. Open it, download a
real .skill, and rotate it to watch the old one get revoked — no account
needed.
Stand up your own in one command (wrangler login first):
pnpm setup:demo # creates D1 + KV, sets the secret, deploys — prints the URL
CI can also auto-deploy on every push to main
(.github/workflows/deploy-demo.yml). Details in docs/DEMO.md.
It's a sandbox: throwaway secret, disposable KV, ephemeral identities. The real paradigm keeps Access in front (see Quickstart).
pnpm install
cp wrangler.toml.example wrangler.toml # fill in ids (see docs/SETUP.md)
wrangler secret put STASH_KEY_SECRET # openssl rand -hex 32
pnpm db:migrate:remote
pnpm deploy
Then put the minting routes behind Cloudflare Access (via the Zero Trust
dashboard, or any CLI that drives the Access API), open the console at your
Worker's URL, and click Download stash.skill. Full walkthrough:
docs/SETUP.md.
After unzipping the download into ~/.claude/skills/stash/:
stash put demo "it works" # ① the minted skill talks to your Worker
stash get demo # → it works
# …click "Rotate key" in the console…
stash get demo # → 401: this skill was revoked ← the whole point
# …re-download, unzip, retry…
stash get demo # → it works again
That rotate → 401 → re-download arc is the entire value proposition in three
commands: a credential you can kill and re-mint, per person, in one click.
There's also a browser path — the console page (/) can put/get/ls
against the protected resource live, so you can see the loop without a terminal.
worker/
index.ts # route wiring: public API (bearer) vs minting (Access)
lib/apikey.ts # HMAC key derivation + the anti-enumeration middleware ★
lib/access.ts # Cloudflare Access JWT verify (+ localhost dev bypass)
routes/resource.ts # the example protected resource — a per-user KV stash
routes/mint.ts # /key, /key/rotate, /skill (zips the bundle + baked .env)
console.ts # the self-contained demo page
skill/ # SOURCE OF TRUTH for the minted skill (SKILL.md + CLI)
scripts/gen-bundle.mjs# snapshots skill/ into the Worker (it has no filesystem)
migrations/0001_init.sql # users(email, key_version) — the salt, never the key
Want this for your infra? Replace routes/resource.ts with your own
endpoints and rewrite skill/ to drive them. The minting + auth machinery
(apikey.ts, mint.ts, access.ts, gen-bundle.mjs) is the reusable core —
that's the part you paste into every project.
SubtleCrypto.timingSafeEqual over fixed-
length digests).STASH_KEY_SECRET is unset.401 and run the same
HMAC + compare work, so membership can't be enumerated by response or timing.key_version is immediate and total for that user, and
invisible to everyone else.MIT — see LICENSE.