diff --git a/website/README.md b/website/README.md
index 6f9af6887e..33c6e1fadd 100644
--- a/website/README.md
+++ b/website/README.md
@@ -4,7 +4,7 @@
>
> — N0zoM1z0
-An English static website with explanatory figures, worked guides and DX-Ball, Notion, TH04 and CTF investigations.
+An English static website with explanatory figures, worked guides and Aegis, DX-Ball, Notion, TH04 and CTF investigations.
The public files are in `website/public/`. The site uses HTML, CSS and small
scripts for copying code, following code comparisons and playing the dinosaur
speed reconstruction. Python
@@ -30,6 +30,7 @@ Open . Refresh the browser after editing a file.
- `public/examples/dino-lab/index.html`: adjustable-speed mini-game, recovered rule, original-game check and browser analysis steps.
- `public/first-investigation/index.html`: a guided Notes export investigation, from setup to a checked CSV prediction.
- `public/showcase/index.html`: the case-study index.
+- `public/showcase/aegis/index.html`: Aegis's Android login-code calculation, with an adjustable clock and reference checks.
- `public/showcase/dx-ball/index.html`: sound-pan investigation and project status.
- `public/showcase/notion/index.html`: Notion's Electron clipboard bridge and rich clipboard format.
- `public/showcase/th04/index.html`: TH04's 16-bit DOS bullet-angle calculation and compiler checks.
@@ -102,6 +103,10 @@ command, then explains what reverse engineering is and why someone would use
it. The manual/agent comparison introduces REA’s role before the examples.
Its opening links directly to Showcases and lets experienced readers skip to
the analysis guides. Case-study previews live on the Showcases page.
+The closing section offers copyable project prompts, from cloning `rea.tools`
+to reconstructing a game from its executable. The experienced-reader shortcut
+lands directly on the guide links below these prompts.
+The page ends with FAQ, Discord and issue-report links under “Any questions?”.
A right-side table of contents stays visible at widths of 1440px and above.
On narrower screens it becomes a sticky, native disclosure; selecting a link
closes the menu and focuses the destination. The homepage script follows
@@ -158,6 +163,21 @@ the website.
[evidence/notion-clipboard.md](evidence/notion-clipboard.md) records the REA
package version, selected findings, source anchors and module-probe scope.
+## Aegis Android case study
+
+The Aegis case inspects the official 3.4.3 APK through REA 6.1.0. It follows
+the default login-code calculation from class search and display references to
+the time block, keyed hash and six-digit output. Selected decompiled Java is
+paired with a readable summary; the original app and excerpts are credited to
+Aegis under its GPL-3.0 license.
+
+`public/assets/aegis-otp.js` is a new teaching reconstruction using the fixed,
+public RFC 6238 test key. `aegis-demo.js` connects it to the clock slider and
+reference-check button. The diagram remains readable and controls stay disabled
+without JavaScript. Keep the demo, selected code and
+[evidence/aegis-login-code.md](evidence/aegis-login-code.md) aligned. The APK and
+analysis-provider JAR remain official external downloads.
+
## TH04 case study
The TH04 case inspects the original PC-98 DOS angle helper through REA 4.1.0.
diff --git a/website/evidence/aegis-login-code.md b/website/evidence/aegis-login-code.md
new file mode 100644
index 0000000000..6ee862139b
--- /dev/null
+++ b/website/evidence/aegis-login-code.md
@@ -0,0 +1,140 @@
+# Aegis Android login-code evidence
+
+Inspection on 9 October 2026, using the published REA 6.1.0 CLI with the
+caller-supplied `jadx-headless-mcp` 0.7.1 provider and OpenJDK 21.0.12.1 on
+Linux x64. The case follows the default time-based code in
+[Aegis Authenticator](https://github.com/beemdevelopment/Aegis).
+
+## Target
+
+- [Official release](https://github.com/beemdevelopment/Aegis/releases/tag/v3.4.3): v3.4.3, published 6 September 2026.
+- Artifact: `aegis-v3.4.3.apk`, 6,881,897 bytes.
+- SHA-256: `fa091c91912c0d56ec3553394fac4158f9a9d178e93bcbb35419601d788cb528`.
+- Package: `com.beemdevelopment.aegis`, version code 82.
+- Reported inventory: 3,077 classes and 1,106 resources.
+- Matching source commit: `d6f4e5925a97e4e91593f1542085eae03432a759`.
+- Android provider JAR SHA-256: `6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`.
+- Provider source revision: `5844800a486d2248fa949b74c9896285f5b54de2`.
+
+The artifact's SHA-256 matches the official GitHub release asset digest and every
+saved REA record. The APK and provider JAR were downloaded into temporary storage.
+They are linked from their official releases; neither file is a website asset.
+
+The CLI used `REA_JADX_MCP_JAR` for its own process. The connected MCP server's
+Android provider was initially unavailable; it was not reconfigured. The page
+expresses the inspected selections as REA tool requests, using the same operation
+parameters as the successful CLI calls. Input paths and result package prefixes
+are shortened for display, as noted beside those blocks.
+
+## REA findings
+
+Twenty REA operations completed successfully against this artifact. These are the
+records used for the page's main findings:
+
+| Operation | Finding | Evidence ID |
+| -------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------- |
+| `inspect_android_method` | Display calls the timestamp overload for TotpInfo. | `ev_c4b51611c987a398359f526dc64866d97a99b146d0ec9177bc1d583a55d64bdc` |
+| `trace_android_references` | TotpInfo is one of the three incoming generator callers. | `ev_6b5457705a67bd4952f7e17630bcf948538424fae5bc2951c08e921af7448d58` |
+| `inspect_android_method` | Eight-byte big-endian counter, account key and HMAC. | `ev_cad4c03faf5b07347cba699d4f8f643c69e5a8f0c85f622fc8cbebe3f834f228` |
+| `inspect_android_method` | Hash-byte selection produces a 31-bit integer. | `ev_5797488db16e72a4e9ad559a8f07e355adc1f73b9eb50566cfb46d47d1b6a883` |
+| `inspect_android_method` | Algorithm name is prefixed with Hmac. | `ev_bffbfa3924c79823168df7daa01e8a3f62c4914d3fb4b54e5d4e60d21e75110f` |
+| `trace_android_references` | Display and copy callers of the base method. | `ev_d90e3ed81b0bb6815826e4edf312eadec91b9d20ff06aaff61d38a191403676b` |
+| `inspect_android_method` | Default SHA1 algorithm and six digits. | `ev_64294487704fa2a9877938017c95163e41c399ba958ea43c5b365118403ae0fe` |
+| `inspect_android_method` | Decimal remainder and leading-zero padding. | `ev_5d0906d19ea9281fed36309794fd5697b54cac3abfbd17e2f78665e758e0eb60` |
+| `inspect_android_method` | Class discriminator 0 selects the OTP formatting branch. | `ev_f0a82295bf6124055b7a12a21533950644267e8e8340038bca9c32db57d8d71f` |
+| `inspect_android_package` | Package identity; 3,077 classes. | `ev_de07111c281cd76c4f2304f42448b06ef3199268815515d59caa02ea2480eb67` |
+| `inspect_android_class` | Both getOtp overloads and their selection indices. | `ev_870577d702741f3023494c1bcb6567381a672a7132a60fd35ea2dd21ab9804c8` |
+| `search_android_classes` | Three matches for the case-sensitive query Totp. | `ev_deeb75a09990e253881d0dc1932097abe00f94447d29a64c782fb48c42e9f492` |
+| `inspect_android_method` | Current clock in milliseconds is converted to seconds. | `ev_ff4d8be5b4797e6f118d37ed55a2f65c9ceb8df39e8058d2cf36cd5561dc4bfd` |
+| `inspect_android_method` | Default period: 30 seconds. | `ev_67af69cd39faceec616a9b1b48b03a5ea71b9c94ff2d1263a0a0063029cc1104` |
+| `inspect_android_method` | Time is floored into a block before generation. | `ev_f282f536e5690e3e2253903bd979687687cfa5a9a4480758b4cd261da145caaf` |
+
+All selected methods returned complete Java representations without a fallback.
+Signatures are provider display signatures; exact DEX descriptors, source-line
+locations and instruction offsets are not available from these results.
+Reference lists are static incoming references, with complete coverage of the
+engine's reported set; reflection and dynamic loading remain unverified.
+
+## Interpretation
+
+`TotpInfo.getOtp()` obtains Unix time in seconds. Its timestamp overload uses
+`floor(seconds / period)` as the generator's counter. The default constructor
+sets the period to 30; the base constructor sets SHA1 and six digits.
+These are defaults, not settings imposed on every imported account.
+
+`getAlgorithm()` constructs `HmacSHA1` for that default. The generator encodes the
+counter as eight big-endian bytes, calculates the keyed hash, uses the low four
+bits of the final hash byte as an offset, and selects a positive 31-bit integer.
+The `OTP.toString()` branch for discriminator 0 reduces it modulo the requested
+power of ten and prepends zeroes. The inspected generator passes discriminator 0
+into the constructor, connecting that branch to the generated code.
+
+The release exposes `generateOTP` and `getHash` under `kotlin.ExceptionsKt`.
+Original-source naming alone would not identify that class in this APK.
+The call returned from `TotpInfo.getOtp(long)` supplies the helper name; REA's
+method inspection and reference tracing establish its calculation and callers.
+
+The base `OtpInfo.getOtp` reference list includes `EntryHolder.getOtp` and
+`MainActivity.copyEntryCode`. Inspecting `EntryHolder.getOtp(int)` then shows that
+the display calls the timestamp overload for a `TotpInfo` account, with an
+optional period offset. The page uses the unshifted code calculation.
+
+## Reconstruction checks
+
+A temporary JVM harness assembled six method bodies directly from saved REA
+results: the timestamp method, algorithm getter, hash method, generator, output
+constructor and output formatter. Imports, enclosing classes, fields, exception
+type and test driver were supplied for the harness. The original APK was not run.
+
+An independently authored Python calculation agreed with those Java methods on
+133 inputs. The website's separately authored WebCrypto implementation then
+agreed with all 133 recorded Java outputs.
+
+The cases include:
+
+- All six [RFC 6238 SHA-1 reference vectors](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B) at eight digits, plus the corresponding six-digit remainders.
+- Every integer second from 0 through 120, checking four complete 30-second blocks and four boundary samples.
+- A leading-zero result at Unix time 1,111,111,109: `081804`.
+
+The fixed key is the public ASCII test string `12345678901234567890`.
+At 30 and 59 seconds the result is `287082`; at 60 seconds it is `359152`.
+The diagram labels these as example clock inputs. A code is recomputed for each
+block; the checked examples change at their boundaries. Adjacent codes need not
+be universally distinct.
+
+The page's “Run reference checks” button tests the browser calculation against
+all six reference inputs at both digit lengths. Slider updates use a revision
+number so an earlier asynchronous calculation cannot overwrite a later input.
+The reference JSON contains public test data, not a captured account key.
+
+## Source comparison and attribution
+
+After obtaining the calculation through REA, selected files from the matching
+release source were read for corroboration:
+
+- [HOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/HOTP.java): counter encoding, HMAC and byte selection.
+- [TOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/TOTP.java): time-to-counter transformation.
+- [OTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java): decimal output and zero padding.
+
+These files confirm the interpretation. The public page's selected decompiled
+Java comes from the APK evidence. It includes fragments from several methods and
+line wrapping for display; the paired readable summary is an explanation.
+The expanded generator panel retains its complete returned body, with formatting
+changes. Aegis excerpts are credited under the upstream
+[GPL-3.0 license](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE).
+The diagram, explanatory summary, demo and its WebCrypto implementation are new
+teaching material; the demo is not recovered original application source.
+
+## Scope and maintenance
+
+The claim is a static APK investigation and a checked reconstruction of its
+default code calculation. It covers neither Android UI execution nor APK
+signature verification, split APK handling, native libraries or vault storage.
+The Android provider requires a Linux or macOS host and a full JDK 17 or later,
+including `jdk.compiler`. The shell walkthrough was executed on Linux with
+JDK 21.
+
+Raw REA results, provider scratch paths, Java harness classes and the downloaded
+binaries remain outside the public website. The public page uses generic filenames
+and the public test key. Update the release identity, selected excerpts, demo,
+reference data and these notes together when moving to another Aegis version.
diff --git a/website/figures.md b/website/figures.md
index c0ab93d609..5cf50ce29f 100644
--- a/website/figures.md
+++ b/website/figures.md
@@ -73,6 +73,18 @@ are new teaching code. Check that the fallback is visible with JavaScript
disabled and hidden when the playable canvas loads. Keep the speed settings,
source excerpt, reconstruction and original-result table consistent.
+## Aegis login-code figure
+
+The Aegis overview and adjustable-clock figure are semantic HTML. The clock
+diagram shows seconds, the 30-second block and the resulting six-digit code,
+using the public RFC 6238 test key. It retains a static 59-second example when
+JavaScript is disabled. The three code selections connect each operation to the
+APK's selected Java and an explanatory summary.
+
+[evidence/aegis-login-code.md](evidence/aegis-login-code.md) records the inspected
+APK, methods, source attribution and reconstruction checks. Keep the block
+formula, default settings, slider outputs and reference cases consistent.
+
## Original image generation prompts
## Prompt 1
diff --git a/website/public/assets/aegis-demo.js b/website/public/assets/aegis-demo.js
new file mode 100644
index 0000000000..74afa79b30
--- /dev/null
+++ b/website/public/assets/aegis-demo.js
@@ -0,0 +1,84 @@
+import { generateExampleCode } from "./aegis-otp.js";
+
+async function initializeAegisDemo() {
+ const demo = document.querySelector("[data-aegis-demo]");
+ if (demo === null) return;
+ const clock = demo.querySelector("input[type=range]");
+ const controls = demo.querySelectorAll("input, button");
+ const status = demo.querySelector("[data-aegis-status]");
+ let revision = 0;
+
+ const updateTime = async (seconds) => {
+ const currentRevision = ++revision;
+ try {
+ const code = await generateExampleCode(seconds);
+ if (currentRevision !== revision) return false;
+ const block = Math.floor(seconds / 30);
+ demo.querySelector("[data-aegis-seconds]").textContent = String(seconds);
+ demo.querySelector("[data-aegis-block]").textContent = String(block);
+ demo.querySelector("[data-aegis-division]").textContent =
+ `floor(${seconds} / 30)`;
+ demo.querySelector("[data-aegis-window]").textContent =
+ `${block * 30}–${block * 30 + 29} seconds`;
+ demo.querySelector("[data-aegis-code]").textContent = code;
+ clock.setAttribute(
+ "aria-valuetext",
+ `${seconds} seconds, time block ${block}`,
+ );
+ status.textContent = "";
+ return true;
+ } catch {
+ if (currentRevision === revision) {
+ status.textContent =
+ "Interactive calculation is unavailable in this browser. The example shows the code at 59 seconds.";
+ }
+ return false;
+ }
+ };
+
+ if (!(await updateTime(Number(clock.value)))) return;
+ controls.forEach((control) => {
+ control.disabled = false;
+ });
+ clock.addEventListener("input", async () => {
+ await updateTime(Number(clock.value));
+ });
+ demo.querySelectorAll("[data-aegis-time]").forEach((button) => {
+ button.addEventListener("click", async () => {
+ clock.value = button.getAttribute("data-aegis-time");
+ await updateTime(Number(clock.value));
+ });
+ });
+
+ const checkButton = document.querySelector("[data-aegis-check]");
+ const checkResult = document.querySelector("[data-aegis-check-result]");
+ checkButton.disabled = false;
+ checkButton.addEventListener("click", async () => {
+ checkButton.disabled = true;
+ checkResult.textContent = "Checking…";
+ try {
+ const response = await fetch(
+ new URL("../showcase/aegis/test-vectors.json", import.meta.url),
+ );
+ if (!response.ok) throw new Error("Reference data unavailable");
+ const { vectors } = await response.json();
+ let passed = 0;
+ for (const vector of vectors) {
+ for (const digits of [6, 8]) {
+ const code = await generateExampleCode(vector.seconds, digits);
+ if (code === vector.sha1.slice(-digits)) passed += 1;
+ }
+ }
+ checkResult.textContent =
+ `${passed} / ${vectors.length * 2} checks passed ` +
+ "(six reference cases, at six and eight digits).";
+ } catch {
+ checkResult.textContent =
+ "Could not run the reference checks. Try again.";
+ } finally {
+ checkButton.disabled = false;
+ }
+ });
+}
+
+await initializeAegisDemo();
diff --git a/website/public/assets/aegis-otp.js b/website/public/assets/aegis-otp.js
new file mode 100644
index 0000000000..0952a6070c
--- /dev/null
+++ b/website/public/assets/aegis-otp.js
@@ -0,0 +1,36 @@
+// Teaching reconstruction of the calculation inspected in Aegis v3.4.3.
+// The key is public RFC 6238 test data, not an account credential.
+const testKey = new TextEncoder().encode("12345678901234567890");
+let importedKey;
+
+/** Generate a code with the public test key and a 30-second time step. */
+export async function generateExampleCode(seconds, digits = 6) {
+ if (
+ !Number.isSafeInteger(seconds) ||
+ seconds < 0 ||
+ ![6, 8].includes(digits)
+ ) {
+ throw new RangeError(
+ "Use a nonnegative integer time and six or eight digits.",
+ );
+ }
+ importedKey ??= crypto.subtle.importKey(
+ "raw",
+ testKey,
+ { name: "HMAC", hash: "SHA-1" },
+ false,
+ ["sign"],
+ );
+ const key = await importedKey;
+ const block = Math.floor(seconds / 30);
+ const counter = new ArrayBuffer(8);
+ new DataView(counter).setBigUint64(0, BigInt(block), false);
+ const hash = new Uint8Array(await crypto.subtle.sign("HMAC", key, counter));
+ const offset = hash[hash.length - 1] & 15;
+ const number =
+ ((hash[offset] & 127) << 24) |
+ ((hash[offset + 1] & 255) << 16) |
+ ((hash[offset + 2] & 255) << 8) |
+ (hash[offset + 3] & 255);
+ return String(number % 10 ** digits).padStart(digits, "0");
+}
diff --git a/website/public/assets/styles.css b/website/public/assets/styles.css
index fc9850f696..d205dab1d2 100644
--- a/website/public/assets/styles.css
+++ b/website/public/assets/styles.css
@@ -261,6 +261,14 @@ section[id] {
background: #38424e;
text-decoration: none;
}
+.button.secondary {
+ border-color: var(--line);
+ background: var(--page);
+ color: var(--ink);
+}
+.button.secondary:hover {
+ background: var(--soft);
+}
.text-link {
font-size: 0.95rem;
font-weight: 500;
@@ -689,6 +697,59 @@ pre code {
.case-figure {
margin-bottom: 3.2rem;
}
+.aegis-step-selector {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+.aegis-step-selector button {
+ min-height: 44px;
+}
+.aegis-calculation .flow-node {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.35rem;
+ min-height: 3.5rem;
+ font-variant-numeric: tabular-nums;
+}
+.aegis-calculation .aegis-code {
+ font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
+ font-size: 1.5rem;
+ letter-spacing: 0.06em;
+}
+.aegis-clock-controls {
+ margin-top: 1.7rem;
+}
+.aegis-clock-controls label {
+ display: block;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+.aegis-clock-controls input {
+ display: block;
+ width: 100%;
+ min-height: 44px;
+ margin: 0;
+ accent-color: var(--accent);
+}
+.aegis-clock-scale {
+ display: flex;
+ justify-content: space-between;
+ color: var(--muted);
+ font-size: 0.75rem;
+ font-variant-numeric: tabular-nums;
+}
+.aegis-clock-controls .actions {
+ gap: 0.7rem;
+ margin-top: 1.2rem;
+}
+.aegis-clock-controls .caption {
+ margin-bottom: 0;
+}
+.aegis-demo .button:disabled,
+[data-aegis-check]:disabled {
+ opacity: 0.6;
+ cursor: default;
+}
.step-selector {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
@@ -1389,6 +1450,31 @@ pre code {
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
+.home-examples-intro {
+ max-width: 760px;
+ margin: 0 0 2.5rem;
+ color: var(--muted);
+ font-size: 1.05rem;
+}
+.home-examples-intro strong {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: var(--ink);
+ font-size: 1.5rem;
+ font-weight: 600;
+}
+.home-project-prompts {
+ margin-block: 2rem 2.5rem;
+}
+.home-project-prompts .agent-terminal {
+ margin: 0;
+}
+.home-project-prompts .caption {
+ margin-block: 0.8rem 0;
+}
+.home-page #analysis-guides {
+ scroll-margin-top: 5rem;
+}
.case-brief {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
@@ -1443,7 +1529,7 @@ pre code {
.comparison-label {
padding-bottom: 0.8rem;
border-bottom: 1px solid var(--line);
- font-size: 0.875rem;
+ font-size: 1.125rem;
font-weight: 600;
}
.agent-investigation > .comparison-label {
diff --git a/website/public/index.html b/website/public/index.html
index e3c9a7b0e5..bb77c7819e 100644
--- a/website/public/index.html
+++ b/website/public/index.html
@@ -71,7 +71,8 @@
Dinosaur game
Calculator
Try REA
- Analysis guides
+ Go deeper
+ Help & feedback
@@ -93,7 +94,7 @@
Already know RE?
- Skip to analysis guides →
+ Skip to analysis guides →
@@ -167,7 +168,10 @@
class="manual-investigation step-comparison"
data-step-comparison
>
-
Before REA · You investigate
+
+ Before REA · You investigate it
+ yourself
+
-
With REA · You ask your agent
+
+ With REA · You ask
+ your agent
+
One prompt.
Ask your agent in plain English.
@@ -385,6 +392,11 @@
+
+ Let's try two examples.
+ Use REA to change a game's speed, then explain a calculator's
+ percentage rule.
+
Example 01 · Chrome’s dinosaur game
Why does the dinosaur get faster?
@@ -863,12 +875,77 @@ SPEED: 6
-
Analyze your software
+
Want to go deeper?
- Choose a guide for your binary, JavaScript app or browser session.
+ Tell your agent what you want to understand or build. With REA,
+ you can work together on anything from
+ cloning this website to
+ reconstructing a game from its executable.
-
+
+
+
+
+
+ ›Use REA to inspect https://rea.tools/. Clone this website
+ for me.
+
+
+
Yes, this one.
+
+
+
+
+
+ ›Use REA to reconstruct this game from its executable.
+ Recover the gameplay logic in C and test it against the
+ original.
+
+
+
+ See the DX-Ball case study →
+
+
+
+
+ For setup and analysis steps, choose your target:
+
+
Native binaries
@@ -896,6 +973,20 @@ SPEED: 6
+