TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Status: finished and archived. QuackTrack was built and submitted in one day on October 25, 2026, and it works as described. The hosted copy on Render was taken down after the event, so there is no live site. Everything runs on your own laptop in two minutes (below), and the hosted mode section tells you how to put up your own copy. The repository is archived, so it is read-only; fork it if you want to build on it.
Hackathon submissions run on forms, shared links, and spreadsheets. Anyone with the link can submit as any team, swap a team's project at the last minute, or say a prompt never arrived. Nobody can prove what changed or when.
QuackTrack treats a FIDO2 (Fast IDentity Online 2) security key, the kind you tap to log in to GitHub, as the team itself.
No accounts. No passwords. No database. The whole event lives in memory on one laptop, or on Render if teams want to use their own phones.
The organizer console tells a first time organizer what to do next, counts submissions from across the room, and shows green only when something is done.
The wrong key is refused in one sentence you can read from the back of the room, and the attempt is logged.
The team room shows the prompt only after the tap, with the deadline, a download, and the submission form below it.
After the signing tap, the room shows the receipt and the console flips the team green.
| Ceremony | Who taps | What the key signs | What you see |
|---|---|---|---|
| Enroll | The team captain, at the organizer desk | A challenge bound to the team | The console row turns ENROLLED with the key's fingerprint. A key already enrolled anywhere is refused. |
| Reveal | Any team member holding the key | A fresh single use challenge for this team's credential only | The prompt appears, the reveal time is logged, and the member who tapped is added to the roster. The wrong key is refused by the browser itself. |
| Sign | The team, holding the key | A challenge derived from the SHA-256 hash of the submission plus a fresh nonce | "You are signing hash 3fa2c9e1" in 72 pixel type, then "Signed. Receipt 3fa2c9e1" and a receipt link. |
A lost or broken key is replaced only through a fourth, deliberately loud ceremony: the organizer taps their own credential, the captain taps the replacement, and the word OVERRIDE shows on the console, the room, the public board, the receipt, and the offline log check. There is no unsigned fallback.
These are the laws the whole codebase is built around, in priority order. Lower numbers win.
verify-log names the broken entry.Some things we did on purpose that are easy to get wrong:
textContent. Post <img src=x onerror=alert(1)> in the chat and you get the text.Node 20 or newer, one security key (or Touch ID for the organizer). No environment variables.
git clone https://github.com/jasonepage/QuackTrack
cd QuackTrack
npm install
npm start
Organizer setup: link the terminal prints and enroll with Touch ID.node scripts/verify-log.js ~/Downloads/quacktrack-log.json. It prints Chain OK.Open exactly http://localhost:3000. WebAuthn (Web Authentication) treats localhost and 127.0.0.1 as different sites, so QuackTrack answers any other address with Open http://localhost:3000 instead.
Local mode is single device. WebAuthn works only on localhost or a real HTTPS origin, so other laptops and phones cannot use the host laptop's localhost as a valid origin. Every team taps in on the host laptop, one team at a time, and clicks Leave room when done. Multi device use needs hosted mode, below.
Plain JavaScript. Node 20, Express 5, and SimpleWebAuthn 13 (server and browser). Those are the only three dependencies. No framework, no build step, no database, no fonts or scripts from outside the server, so local mode works with the Wi-Fi off. About 7,300 lines of code and pages, plus about 1,800 lines of design documents.
In one day, from a specification first. CLAUDE.md, GENRE.md, PRODUCT.md, SRS.md, and SDS.md were written before the code and are the project's memory: the laws above, every requirement with its failure case, the 82 error sentences, the state machines, the challenge construction byte by byte, the threat model with accepted risks, and the build tickets with cut lines. The code was written with Claude Code against those documents, one ticket per session, and every ticket's "Done when" test was run with two physical keys before the next started. The documents are in the repo and are the best place to start reading.
Teams ask questions about the event in their room. The assistant may only answer by quoting the event information the organizer saved on the console (Load demo data saves a sample). If the answer is not in it, the team sees "I can't find that in the event information, so I've sent your question to the organizers." and the question shows on the console under "Questions it could not answer".
AI_PROVIDER=gemini GEMINI_API_KEY=paste-key-here npm start
The model is the constant GEMINI_MODEL in src/ai/gemini.js (gemini-3.8-flash). If the console shows "AI: Gemini, unavailable" and the terminal shows a model error, change that one line to gemini-3.5-flash-lite.ollama pull gemma4:e2b, then AI_PROVIDER=gemma npm start. Nothing leaves the laptop.If a call fails or takes over 15 seconds, that one question falls back to the list and the next question tries the AI again. The console shows the AI status from the last call.
The same code with only environment variables changed. Teams use their own laptops and phones at the HTTPS address.
The repo has a Render Blueprint, render.yaml. It holds no secret values.
In the Render dashboard click New, then Blueprint, and connect this repo. Render reads render.yaml: one web service named quacktrack on the 0.5c-512mb instance (about $7 a month) in Oregon, built with npm ci and started with npm start.
Render asks for three values (never put them in a file in the repo):
ORIGIN: https://<your-service>.onrender.comRP_ID: <your-service>.onrender.comGEMINI_API_KEY: your own Gemini keyRender makes SESSION_SECRET itself, and AI_PROVIDER is already gemini.
When the deploy finishes, look at the address at the top of the service page. If Render gave it a different name (for example quacktrack-ab12.onrender.com), change ORIGIN and RP_ID to match under Environment, save, and deploy again.
Open the Logs tab and copy the Organizer setup: link (see below).
Do not use the free instance: it sleeps after 15 minutes without visitors, and a restart wipes the event. Pushing to main does not redeploy (autoDeployTrigger: off), because every deploy restarts the server and wipes the event; deploy by hand with Manual Deploy before the event starts. Delete the service after the event to stop paying.
When ORIGIN starts with https://, cookies carry the Secure flag and Express trusts one proxy hop. A Render restart or redeploy wipes the event.
Keys must be enrolled again in each mode. A credential is bound to its RP ID (relying party identifier). A key enrolled on localhost does not work on the Render origin, and the reverse. Enroll the organizer and every team fresh on the hosted origin.
QuackTrack reads only these 8 variables. Every one has a default, so npm start works with none set.
| Variable | Default | What it does |
|---|---|---|
PORT | 3000 | Port the server listens on. |
RP_ID | localhost | WebAuthn relying party ID. Must be the host in ORIGIN or a parent domain of it. |
ORIGIN | http://localhost:<PORT> | The exact address people open. Requests for any other host are refused. |
SESSION_SECRET | 32 random bytes per boot | Signs session cookies. When unset, the terminal prints SESSION_SECRET not set: sessions end when the server restarts. |
AI_PROVIDER | unset | gemini or gemma for the FAQ assistant. Unset means the FAQ shows the searchable event information list. |
GEMINI_API_KEY | unset | Your own Gemini API key, used only when AI_PROVIDER=gemini. |
OLLAMA_URL | http://localhost:11434 | Ollama address, used only when AI_PROVIDER=gemma. |
OLLAMA_MODEL | gemma4:e2b | Ollama model name, used only when AI_PROVIDER=gemma. |
If the host in ORIGIN is not RP_ID and does not end with a dot plus RP_ID, the server prints RP_ID must match the host in ORIGIN. QuackTrack stopped. and exits, because WebAuthn would refuse every tap.
Never write a secret to a file, including a git ignored .env. Paste it inline into the one command that needs it, for example:
AI_PROVIDER=gemini GEMINI_API_KEY=paste-key-here npm start
On every boot with no organizer enrolled, the server prints a one time link to its own terminal:
Organizer setup: http://localhost:3000/setup?token=<token>
Open it on the host laptop and enroll the organizer credential, usually a passkey with Touch ID, so both physical keys stay free for teams. The link works once. After that, the organizer logs in on the console with a tap. Anyone who can read the server terminal can read the link, so keep the terminal on the host laptop. A restart wipes the event and prints a new link.
Before the clock starts:
npm start on the stage laptop and open http://localhost:3000/console in Chrome. Log in with the organizer passkey.RESET, and click Reset event. Write down the key list first.jasonepage and a display name. Do not click Join yet.jasonepage), full commit SHA from git rev-parse HEAD, and description ready to paste.On the clock (SRS.md section 8):
| Time | Do this | You should see |
|---|---|---|
| 0:00 | Show the console. | "0 of 2 submitted", a green "Log verified" badge, two grey DRAFT rows saying "Not enrolled". |
| 0:05 | Enroll with key on Mallard, tap key A (yellow). | Mallard: ENROLLED, Security key, Pool 01 (yellow). |
| 0:12 | Enroll with key on Teal, tap key B (blue). | Teal: ENROLLED, Security key, Pool 02 (blue). |
| 0:18 | Assign Duck Pond Sensors to Mallard and Quack Commons to Teal. | Both rows show their track and a red "Not revealed". |
| 0:24 | In the team window click Join and tap key, tap key B. | Red banner: "That was not Team Mallard's key, or the tap was cancelled. Nothing was revealed." |
| 0:31 | Click Join and tap key again, tap key A. | The room shows Duck Pond Sensors. Mallard turns green "Revealed", with jasonepage as Captain. |
| 0:37 | Paste the four fields, tick the declaration, click Sign submission. | "You are signing hash ........ Tap your key." in large type. |
| 0:48 | Tap key A. | "Signed. Receipt ........" and an Open receipt link. |
| 0:52 | Point at the console. | "1 of 2 submitted", Mallard green SUBMITTED with the same hash and "Owner on roster", Teal red "Not submitted", "Log verified". |
The server forgets everything when it stops, so this sheet is the record of which key went where. Print it and fill it in at the desk.
Tag rule: attach a bright tag to each key. Write only the team name and "Return to the organizer desk" on the tag. Never write a credential ID or PIN on it.
| Pool label | Tag color | Team | Checked out | Returned | Reset done |
|---|---|---|---|---|---|
| Pool 01 | yellow | ||||
| Pool 02 | blue | ||||
| Pool 03 | |||||
| Pool 04 | |||||
| Pool 05 | |||||
| Pool 06 |
After the event, reset every returned key with its maker's tool before the next event. For a YubiKey: ykman fido reset. This clears any PIN a team set and any passkeys they saved for other sites. QuackTrack does not store its own credentials on the key, and the console does not track returns in this version. An unreturned key never blocks a submission.
QT_TEST.tamperSign('changed') (SRS.md INV-14) reads the values in the sign form, so fill the form first. Browsers only open a key prompt for a page that has focus, so run it from the developer console with a delay and then click a blank part of the page: setTimeout(() => QT_TEST.tamperSign('changed'), 3000). Without the delay the browser refuses at once, with no prompt, and the room shows "The submission was not signed. Nothing was submitted."api('/api/sign/options', { body: { teamId: '<team ID from the console log>', projectName: 'x', repoUrl: 'https://github.com/owner/repo', commitSha: 'a'.repeat(40), description: 'x', declared: true } }).# section markers.MIT. See LICENSE.