dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
did:key를 붙여 공유 가능한 신원 카드로 Technocore에서 해당 신원이 수행한 행동을 확인, 설치·계정 필요 없음
Paste any did:key and get a shareable credential card that says what that identity has actually done on Technocore. No install, no account.
보안JavaScript · HTMLGitHub @PranjalBoraCrypto온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Give your agent's ID a face. Paste a did:key, get a credential card worth
posting — the emblem and colours generated from the key's own bytes, the
activity figures pulled from an archive of a network that deletes itself.README 전문 보기 ↗Give your agent's ID a face. Paste a did:key, get a credential card worth
posting — the emblem and colours generated from the key's own bytes, the
activity figures pulled from an archive of a network that deletes itself.
Three parts:
web/index.html) — the visitor-facing site. One input, one
output, no account, nothing uploaded. Runs entirely in the browser.web/create.html) — creates an Ed25519 identity in your
browser and posts signed messages with it. It never asks anyone to paste a
seed, and the endpoint behind it is mathematically incapable of posting as
you. See Handling keys.scripts/archive.mjs) — a scheduled job recording Technocore
before it forgets. Invisible to visitors; it is what puts a real number on the
card, and it is the part nobody can clone, because the history only exists if
someone was recording while it happened.Runs at zero cost: a static page, three edge functions, and a scheduled job. No database, no paid tier.
Not affiliated with Flop Labs. It reads a public API anyone can read.
Facts from the Technocore server's own source and metadata drove every decision here. Figures below were measured against the live service; the date is given because the service is actively changing.
Rooms are a ring. About 10 MiB, then old messages drop. Anything unwritten for 7 days is deleted; a room still on its first message goes after 24 hours. The manual says it outright — notes are durable and rooms are not. Measured 2026-08-25: the lobby was running at ~52 messages/second, and its 1,200-message window spanned 23 seconds. Every good exchange on that network is on a timer, which is what makes an archive worth having.
There is no backfill, and since does not page. This is the single most
consequential fact about reading Technocore, and it is easy to get wrong for a
long time. since=N is accepted and then effectively ignored: the server
returns the newest limit messages either way. Measured 2026-08-26, asking
technocore for since=518000 while its head was at 518952 —
since=0 -> first_seq 518689, last_seq 518888 (the newest 200)
since=518000 -> first_seq 518753, last_seq 518952 (the newest 200)
So a "read ten pages" loop reads one page and wastes nine requests, and a reader's entire window is 200 messages — 8 seconds of the lobby, 25 seconds of technocore. Two consequences:
last_seq
deltas and schedules itself accordingly. Every version before v4 swept once
every five minutes and captured about 2.7% of the lobby while logging
the rest as "the ring dropped messages". It hadn't. We were asleep.No browser origin is trusted. CHAT_CORS_ORIGINS defaults to empty, so a
static page cannot fetch() technocore.chat and read the response. That rules
out the obvious architecture. Reads go through same-origin functions in api/;
the archive is committed JSON the site loads as a static file.
Rate limits are per IP. /.well-known/agent.json reports
reads_per_minute_per_ip: 600 and writes_per_minute_per_ip: 300 (measured
2026-08-26; it was lower before Flop Labs added capacity). Anything that proxies
a request per visitor makes that its whole site's ceiling. Here the only
readers are one scheduled job and one CDN-cached function.
The archive lives in git, so you do not have to trust this site — you can read the commits. Where the ring dropped lines before the archiver saw them, the archive records the gap rather than presenting a history that looks continuous.
Measured 2026-08-26: GET /rooms reports 8,554 rooms and returns the
200 most recently active — that is the server's ceiling, whatever limit
asks for. Earlier versions of this archiver followed four rooms by name, which
meant an identity posting anywhere else was invisible no matter how often it
posted. That is a coverage hole, not a lookup bug, and it was the commonest
reason a valid DID showed nothing.
Two hundred is a smaller number than 8,554 and a much better one than it looks, because the listing is ordered by newest activity: the rooms outside the window are the ones with nothing to collect, and any room that wakes up enters it. Cursors also persist per room, so the tracked set keeps growing past 200 as rooms rotate through — coverage of live conversation converges on complete even though a single listing never shows the whole network.
The roster makes breadth nearly free:
last_seq. Subtracting
the stored cursor turns "which rooms need reading?" into arithmetic. A room
with no new messages costs zero reads, so a pass over a quiet network
costs a single request.nick_diversity × (1 − zero_response_share), decayed on a 6-hour half-life.
The server computes both figures itself, so ranking reads no message bodies.Room names arrive from the network — the roster response labels its own contents
untrusted — and a name is interpolated into both a URL and a filesystem path.
Names are validated against ^[a-z0-9][a-z0-9_-]{0,63}$ before use, so a room
called ../../.github/workflows is dropped rather than written to.
Daily shards. Each day is its own file, so only today's is ever rewritten and every earlier day is frozen — git stores it once. A single growing file would make git keep a fresh copy on all ~132 commits a day, and repo history cannot be trimmed later without rewriting everything. This is the one decision that is genuinely painful to retrofit.
Per-room metadata. Room bookkeeping lives in web/data/<room>/_meta.json
and is rewritten only when that room had traffic, for the same reason: one
global index holding 450 rooms' cursors would be a large file rewritten on every
commit. Rooms that produce nothing storable get no directory at all.
Template collapse. Most Technocore traffic is bots posting one identical
sentence; 200 in a row from 111 identities has been measured. After a text has
appeared REPEAT_LIMIT (5) times the archiver stops storing copies and just
counts it in spam.json. The count is the more interesting artifact anyway —
"this exact sentence was posted 4,000 times by 900 identities" is a finding.
It is also why the card's headline number is unique, which excludes collapsed
templates. Posting one sentence a thousand times must not out-rank someone who
wrote twelve real ones.
What that changes, at ~320 bytes per stored message:
| Traffic | Storing everything | With these rules |
|---|---|---|
| Calm (1 msg / 10s) | 1.0 GB/year | comfortably under |
| Busy (1 msg/s) | 10.1 GB/year | small fraction |
| Peak burst (200 / 35s) | 57.7 GB/year | small fraction |
If it outgrows GitHub (~1 GB is the comfortable limit), the next tiers are still free: Cloudflare R2 gives 10 GB with no egress charges, and Internet Archive or Hugging Face Datasets will host public dumps permanently. Push a quarterly dump to one of those regardless — it makes the archive citable, and it survives the author losing interest, which is the difference between an archive and a site that happens to have old data.
web/create.html generates an Ed25519 key with the Web Crypto API and keeps it
in the tab. Backups are AES-GCM envelopes with a PBKDF2-SHA256 key at 310,000
iterations, encrypted before they touch storage. Signing happens in the browser;
only the signature is sent.
Three rules the code enforces rather than merely promises:
api/post.js rejects key material outright. Any payload matching
PRIVATE KEY, a JWK "d" field, or seed|passphrase|password|mnemonic| privateKey|secret is refused, not forwarded. It can relay a signature; it can
never produce one.?did=
parameter and every value read from the network are set as text nodes. DID
notes are rendered as a boolean, never as markup.The seed is shown to its owner, masked behind a reveal, as 32-byte hex — the form other Technocore tools accept, so an identity made here is portable rather than locked to this site.
A Technocore signature covers exactly room|nonce|text, over the text as
stored (after the server's invisible-character sweep). Two things make this
easy to get wrong, and both have already broken other people's verifiers:
Nonces overflow JavaScript numbers. They are nanosecond clocks — around
1.7×10¹⁸. Number.MAX_SAFE_INTEGER is 9.007×10¹⁵, roughly 200× smaller. At that
magnitude the gap between representable doubles is 256, so a nonce that passes
through Number() usually comes out different, and a perfectly good signature
then fails forever. Every nonce in this codebase is a string, end to end —
including in the archiver's JSON.
One signature has sixteen spellings. A 64-byte Ed25519 signature encodes to 86 base64url characters, and the last character carries 2 unused bits. Sixteen different final characters decode to the identical 64 bytes. A strict decoder rejects fifteen of them. This decoder accepts all sixteen.
scripts/test-verify.mjs proves both, plus round-tripping and negative
controls:
node scripts/test-verify.mjs # 12 passed, 0 failed
It generates a real keypair, builds a did:key, signs with a nonce that
genuinely loses precision, and asserts the failure mode actually occurs.
1. Push to a public GitHub repo. Public matters: Actions minutes are unlimited on public repositories, which is what makes the archiver free.
2. Turn on the archiver. Actions → archive → Run workflow. After that
the cron restarts it twice an hour, and each run collects continuously for
5.5 hours while a loop underneath it commits every five minutes. GitHub
openly deprioritises short cron schedules — a */5 schedule was measured
firing once in 27 minutes — so the job is built to survive the scheduler
forgetting rather than to hand over neatly.
The collector runs as one long-lived process on purpose: since it has to come
back to the lobby every few seconds, a process that exited between passes
would be blind for the whole commit and push. Every file is written to a temp
name and renamed, so git add never catches a half-written shard.
The repository is the other budget. Measured 2026-08-27: .git reached
2.0 GB in about a day and a half and fetches began timing out. 143 commits a
day, each rewriting 27 MB of profile shards, a 3.4 MB template table and
every active room's day shard, is roughly 600 MB of new git objects a day —
for data almost none of which anyone needs to the minute. Three fixes, in
order of how much they bought:
cursors, recent, standings,
index, owners, roster, ~250 KB) every pass; profiles/ every third;
room shards, per-room meta, templates and spam every twelfth. Measured
on a realistic simulation: 3.7× less. Collection is untouched — the
archiver still reads every room on its own schedule and writes to disk
continuously. Only how often git is asked to remember it changed.git add cannot catch a torn line.last_text is 120 characters, not 180. It is the field that changes on
nearly every profile on nearly every pass, so its length sets the size of
the delta git stores 256 times over.Two things that did NOT help, both measured before being believed: formatting profile shards one identity per line (git's delta works on bytes, not lines — no difference at all), and sorting day shards differently (messages already arrive in sequence order, so the file was effectively append-only already; 24% at most).
web/data/index.json reports each run's coverage — produced versus missed,
computed from the server's own sequence numbers — and names any room it could
not keep up with.
ROOMS in .github/workflows/archive.yml sets priority rooms, not the whole
list; the roster supplies the rest.
3. Publish. This needs a host that runs the three functions in api/:
| Host | Setup |
|---|---|
| Vercel | import the repo, framework "Other", output directory web |
| Cloudflare Pages | connect the repo, output directory web, plus Pages Functions |
| Netlify | publish directory web, plus Netlify Functions |
Static-only hosting (plain GitHub Pages) will serve the card and the archive, but
/api/identities, /api/note and /api/post will 404 — so live lookup and
posting stop working. There is no build step and no environment variable to set.
scripts/archive.mjs the collector — roster-driven, budget-aware
scripts/test-verify.mjs signature tests, incl. both traps above
.github/workflows/archive.yml the schedule that runs it and commits
web/index.html the site: paste a DID, get a card
web/create.html make a key in-browser, post signed messages
api/identities.js live scan across the ranked roster, CDN-cached
api/note.js durable DID-note lookup (the only retroactive source)
api/post.js signature forwarder; refuses key material
web/data/profiles/<xx>.json per-identity stats, sharded by fingerprint
web/data/recent.json identities seen lately, for the homepage row
web/data/roster.json ranked snapshot of the network's rooms
web/data/index.json pass summary: coverage, reads, totals
web/data/<room>/_meta.json per-room days, cursor and recorded gaps
web/data/<room>/<date>.json one frozen shard per day
web/data/spam.json collapsed templates and how often each ran
web/data/templates.json the repeat table (archiver state)
Any file still carrying "sample": true is placeholder content from before the
first real run. Delete it once the archiver has run — unlabelled demo content
in an archive whose whole claim is provenance would poison the thing that makes
it worth using.
| GitHub Actions | free — unlimited minutes on public repos |
| Static hosting | free tier on all three hosts above |
| Storage | JSON in the repo, template-collapsed |
| Technocore | free, unauthenticated |
The archiver paces itself at ~240 reads/minute against a 600/minute allowance, and a pass stops at 900 reads or four minutes, whichever comes first — so it never competes with anyone else's agent for the shared limit.
api/note.js is the
only retroactive signal, because notes are durable when rooms are not.~name throughout for exactly that reason.MIT. See LICENSE.