ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A Capture The Flag (CTF) challenge created for educational purposes at Inatel cas@viva, focused on logic, observation, and basic cryptography. It features interactive levels where users analyze clues and submit flags, promoting hands-on learning and interest in information security.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
An educational Capture The Flag (CTF) built for the students of Inatel cas@viva. Players join Operação cas@viva, a five-stage digital investigation in which each stage teaches one security skill and leaves a clue for the next. The game interface is written in Brazilian Portuguese, the language of its audience. Code, comments, documentation and commit messages are in English.查看完整 README ↗
An educational Capture The Flag (CTF) built for the students of Inatel cas@viva. Players join Operação cas@viva, a digital investigation in five stages, each one teaching a security or reasoning skill. Every player gets one official, server-validated attempt, scored on accuracy and speed, with a live ranking and a printable certificate.
The game interface is written in Brazilian Portuguese, the language of its audience. Code, comments, documentation and commit messages are in English.
Origem
│
┌─────────────┼──────────────┐
▼ ▼ ▼
Galeria Sequência Interceptação ← any order
└─────────────┼──────────────┘
▼
O Cofre
│
▼
Certificate
| # | Challenge | Skill | What the player learns |
|---|---|---|---|
| 00 | Origem (Origin) | Careful reading | A black bar hides data, it does not erase it |
| 01 | Galeria (Gallery) | Visual analysis | Steganography: information hidden inside images |
| 02 | Sequência (Sequence) | Pattern recognition | Finding the rule behind apparently random data |
| 03 | Interceptação (Interception) | Substitution cipher | Breaking a Caesar cipher |
| Final | O Cofre (The Vault) | Telling signal from noise | An investigation room with three documents: picking the one that matters, decoding binary to text and connecting the clues to open a 4-digit vault |
Gameplay
Tools
Quality
prefers-reduced-motion.Requires Node.js (current LTS recommended) and npm.
git clone https://github.com/tiagoosm/ctfcasaviva.git
cd ctfcasaviva
npm install
npm start
The development server runs at http://localhost:3000. It uses the project's Supabase backend by default; see Backend to point it at your own.
| Command | Description |
|---|---|
npm start | Starts the development server (npm run dev is an alias). |
npm test | Runs the tests in watch mode. |
npm run test:ci | Runs the full test suite once, including the end-to-end playthrough. |
npm run build | Creates the production build in build/. |
npm run hash-flag -- <challenge-id> <answer> | Prints the salted hash of an answer for a challenge config. |
src/
├── challenges/ # one folder per challenge: config (index.js) + Stage component
│ ├── vault/ # investigation room: vault keypad and case files
│ └── index.js # challenge registry and unlock tiers
├── components/
│ ├── challenge/ # ProgressTrack, MissionTimer, ChallengeHeader, FlagForm, HintPanel, SuccessPanel
│ ├── layout/ # AppLayout, Header, Footer, BrandMark
│ ├── ui/ # Button, Modal, ConfirmDialog, Select, FeedbackMessage…
│ └── ImageViewer.js
├── admin/ # admin area (lazy-loaded): login, players, stages
├── api/ # backend client, plus an in-memory fake used by the tests (__mocks__)
├── game/ # reducer, selectors, scoring, names, classes, persistence, provider
├── hooks/
├── pages/ # Landing, Mission map, Challenge, Completion, Ranking, 404
├── styles/ # Tailwind base layer and global styles
└── utils/ # sha256, answer normalization/validation, formatting
scripts/
└── hash-flag.mjs # answer hash generator
supabase/
└── migrations/ # database schema and server-side functions, in order
The look is an investigation board: blue felt with paper sheets, photos, sticky notes and label-maker tape pinned to it, and an orange thread linking the stages on the mission map. Design tokens (cas@viva orange #E87000, blue #004898, the felt and paper surfaces, fonts and animations) live in tailwind.config.js and src/styles/index.css. Surface colors are CSS variables, so the same component reads correctly on the board and on paper.
Challenges are grouped in tiers, defined in src/challenges/index.js and mirrored by the tier column of ctf_challenges on the server:
| Tier | Challenges | Unlocks when |
|---|---|---|
| 1 | Origem | The attempt starts |
| 2 | Galeria, Sequência, Interceptação | Origem is solved |
| 3 | O Cofre | All of tier 2 is solved |
A challenge opens once every challenge of an earlier tier is solved; inside a tier the order is up to the player. The status of each stage (solved, available, locked) is derived from the saved progress of each challenge, so there is no "current stage" to keep in sync.
The rule is enforced in two places. The interface sends a player who opens a locked stage by URL back to the map with the reason. The server refuses entering, asking for the hint or answering a locked challenge, so the rule holds even when the API is called directly.
A solved stage can be opened again to review it, but it cannot be answered again: its score is recorded once.
The server has the final word on every answer: ctf_submit compares it with its own hash, and only then records the stage as solved.
ctf_challenges.The challenge configs also carry these hashes, plus hashes of near-miss answers. The browser uses them only to pick a helpful message ("you are close"), never to accept an answer.
The Vault ships no hash at all: a 4-digit combination has only 10,000 possibilities, so it is checked by the server alone, which also locks the stage after each wrong combination (3 s, then 15 s from the fifth error and 60 s from the tenth). Its hash and plain-text answer are set directly in the database, not in the migrations.
Solutions exist in readable form only in the test files, which are not part of the production bundle. SHA-256 is implemented in src/utils/sha256.js rather than through Web Crypto, because crypto.subtle is only available in secure contexts and the game also needs to run over a local network.
The mission is worth 1000 points: 100 for Origem, 200 each for the Gallery, Sequence and Interception, and 300 for the Vault. For each challenge:
| Component | Rule |
|---|---|
| Base | 70% of the challenge, earned by solving it |
| Speed bonus | Up to 30%, based on the time spent in that challenge. Full up to the challenge's fast time, then decaying linearly to zero at its slow time |
| Wrong answers | −10 each, charged for at most five |
| Hint | −25 |
| Floor | A challenge never scores below zero |
A challenge's score is computed by the server when it is solved. The order in which the investigations are played does not change anything.
Time only counts while the player is inside a challenge. Each challenge has its own clock, kept by the server, which runs while its page is open and pauses when the player goes to the map, the ranking or anywhere else. A heartbeat every 30 seconds keeps it running; a clock that stops hearing from the browser is closed at the last heartbeat, so a closed tab does not keep counting. That active time alone decides the challenge's speed bonus.
The mission clock, pinned to the top right corner of the challenge pages, is the sum of the challenge clocks. It is hidden outside the challenges, where it is paused anyway, and stops for good when the last challenge is solved. The total time is shown on the certificate and breaks ties in the ranking.
The formula lives in src/game/scoring.js and is mirrored by ctf_score on the server.
The backend is Supabase (Postgres). The schema and all server logic are in supabase/migrations, applied in order.
The browser cannot read or write the tables: row level security is enabled with no policies. It can only call a few database functions, and the server is the one that checks each answer, measures time with its own clock, counts errors and computes the score. A client therefore cannot submit a score, a time or an error count.
| Function | Purpose |
|---|---|
ctf_start | Creates the player's attempt, or returns the one that already exists |
ctf_state | Returns the current state of an attempt |
ctf_enter | Starts or resumes the clock of a challenge; also the heartbeat |
ctf_leave | Pauses the clock of a challenge |
ctf_hint | Records that the hint was used |
ctf_submit | Validates an answer; on success stores time and score, and freezes the result on the last one |
ctf_ranking | Returns the Top 20 described in Ranking |
Every call that touches a challenge goes through the same check of the progression rules.
A player is identified by full name and class. Names are stored and shown in capital letters, and the server compares a normalized form of them (case, accents and extra spaces are ignored), so João da Silva, joao da silva and JOÃO DA SILVA are the same player. A unique index on that key guarantees a single attempt per player, even under parallel requests.
localStorage is only a cache, rebuilt from the server when the app opens.completed and its score, time, errors and hints are frozen. A completed attempt cannot be entered, answered or started again.Because identity is just name and class, anyone who types another player's name and class picks up that player's attempt. That is a deliberate trade-off to avoid accounts and passwords for students.
The backend is required to play: nothing starts and no answer is accepted unless the server confirms it.
Known limit: the challenge content ships with the site, so someone who was told the answers can finish quickly. The server bounds every score to what the rules allow, but it cannot tell a fast solver from an informed one.
To point the app at another Supabase project, apply the migrations there, set the Vault's answer in ctf_challenges, and set REACT_APP_SUPABASE_URL and REACT_APP_SUPABASE_KEY (the publishable key) at build time.
The public ranking shows the Top 20 among everyone who started, completed or still playing, and refreshes every 30 seconds.
The certificate is about completing the CTF, so it shows no ranking position.
The admin area lives at /admin. It is not linked from the player interface and is loaded as a separate bundle only when that route is opened.
Access is enforced by the server, not by hiding the route. Administrators sign in with Supabase Auth (email and password), and every ctf_admin_* database function checks that the caller's confirmed email is listed in the ctf_admins table. Players never sign in, and name and class are identification only. Plain-text answers are stored in the database and returned only to administrators.
To set up an administrator:
ctf_admins (SQL editor: insert into ctf_admins (email) values ('[email protected]');).Create src/challenges/<name>/index.js with the config (id, slug, code, title, points, fastSeconds, slowSeconds, objective, answerMode, hint, success, Stage) and a Stage component.
Generate the answer hash and paste it into answerHash. Use the same command for nearMisses[].hashes.
npm run hash-flag -- <challenge-id> <answer>
Add the challenge to the array in src/challenges/index.js and give it a tier in TIERS. Routing, the mission map, the progress track and scoring adapt automatically.
Add a row for it to the ctf_challenges table with the same id, display order, tier, points, time window and hash, plus the plain-text answer for the admin area, so the server can check and score it.
The app is a static single-page application hosted on Vercel, deployed automatically from main. vercel.json rewrites every route except /api/ to index.html so client-side routing works on direct access and reload.
On the free plan Supabase pauses a project that gets too little database activity in a week (its docs suggest a few requests each day), and the CTF is only used now and then. Four Vercel Cron Jobs in vercel.json call api/keepalive.js every 6 hours (03:00, 09:00, 15:00 and 21:00 UTC; on the Hobby plan each job runs once a day, within the hour). Each call makes a few read-only requests; nothing is written.
A project that is already paused is not woken up by these reads: it has to be restored once in the Supabase dashboard. Any static host with an equivalent rewrite rule can serve the contents of build/.
Developed by Tiago Machado for Inatel cas@viva.
This project is intended for educational purposes.