daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

commit 18e9f45dce9a9126f5aab0732fb4187f1eec5dfe
parent 91fe41ea0ca5497221c890afda7aa580fef60db8
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Sun,  4 Oct 2026 06:16:07 +0100

Design the signal-field port onto the section banners

The interactive dot field from daemon-sec's ambient-signal.tsx, at
DEFAULT_STYLE = 2, lifted out of React and onto the cheatsheet's dark
banner plate.

Settled during brainstorming: banners only, so HeroLoop keeps the home
hero and its 21.4s canvas to itself; the constellation sits behind the
existing fuzz field rather than replacing it, with --fuzz-gain pulled
down in CSS so the lattice reads under the streaks; and the palette
comes from the theme-relative tokens the plate already pins, which
retires the tone prop.

Records three premises from the task brief that the repo contradicted —
the dawn/night tokens do exist, app.ts does not drive the banners, and
the home hero is HeroLoop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Diffstat:
Adocs/superpowers/specs/2026-10-04-signal-field-banners-design.md | 305++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 305 insertions(+), 0 deletions(-)

diff --git a/docs/superpowers/specs/2026-10-04-signal-field-banners-design.md b/docs/superpowers/specs/2026-10-04-signal-field-banners-design.md @@ -0,0 +1,305 @@ +# Signal field on the section banners — Design + +**Date:** 2026-10-04 +**Source:** `/Users/daemon1/git/daemon-sec/client/src/components/daemon/ambient-signal.tsx` (716 lines, React + WebGL) +**Prior audit:** [`docs/design-pass-findings.md`](../../design-pass-findings.md) §1 +**Bookmark:** `signal-field-banners`, off `main` + +--- + +## 1. Intent + +Bring daemon-sec's interactive dot field — `ambient-signal.tsx` at `DEFAULT_STYLE = 2`, the +"constellation" — onto the cheatsheet, which is Astro 7 with no React and a contract +(`DESIGN.md`) forbidding new dependencies. + +The field is a network map: hosts on an irregular lattice that drift on their own and are pushed +away from the pointer. Hosts within the cursor's reach lock to the archive's REC red and the links +between them light, so pointing at the page draws an attack path across it. In style 2 the dots are +dense and plain, links appear *only* near the cursor, and the cursor reaches out to them. + +Success looks like: landing on any category page, the dark banner plate carries a lattice of faint +dots, and moving the pointer across it draws lit paths that settle when the pointer leaves — with +the banner title no less legible than it is today, and no regression to the fuzz field already +painting that plate. + +### What the user asked for, verbatim + +> "Port daemon-sec's interactive dot field to the daemon-sec-cheatsheet Astro site." + +> "Open question to settle with the user: dots on every page, or only the home hero and category +> banners?" + +On the composition question, asked during brainstorming: + +> "i dunno you choose whatever looks cooler" + +### Decisions taken during brainstorming + +| Question | Decision | +|---|---| +| Where it mounts | **Section banners only** — every route below home. Home page untouched. | +| Relationship to the existing fuzz field | **Both**, constellation behind, fuzz gain reduced on the banner so the lattice reads | +| Palette source | **Theme-relative tokens** read off the canvas (`--text`, `--*-soft`), not the absolute `--color-night-*` set | +| `tone` prop | **Dropped** — `.plate` already pins night for the subtree | +| Shader | **Lifted verbatim**, all four styles retained, `?field=N` override kept | +| Fallback | **No canvas** — the fuzz field carries the plate alone. `ambient-field.tsx` is not ported. | + +### Premises in the original task brief that did not survive contact with the repo + +Recorded because each one changed the design: + +1. **"`--color-dawn-*` / `--color-night-*` do not exist in the cheatsheet."** They do — + `src/styles/tokens.css` carries the whole "DÆMON main-site palette" block, extracted verbatim + from daemon-sec, and `HeroLoop.astro` already reads it. Only the three dawn `-soft` variants are + absent, and the original's `SOURCES` table already falls through to full strength when a `-soft` + is missing. The mapping problem the brief anticipated does not exist. §4 takes a different route + again, for reasons of its own. +2. **"`src/scripts/app.ts` drives SectionBanner, Marquee and Hero."** It does not. Each of those + components carries its own inline `<script>` with its own `astro:page-load` / `astro:before-swap` + pair; `app.ts` has no `before-swap` teardown at all. The pattern to follow is + `SectionBanner.astro:240-289`, not `app.ts`. +3. **"The home hero."** The home page uses `HeroLoop`, not `Hero`, and `HeroLoop` is already a + heavy canvas animation — an authored 1920×1080 bitmap on a 21.4s rAF clock filling the whole + band. This is the main reason the scope question resolved to banners only. + +### Assumptions + +- The banner is near the top of every page it appears on, so the original's `u_scroll` term + (`window.scrollY / box.height`, written for a hero at page top) carries over unchanged. +- Nothing in the banner needs the `data-index-zone` clearing; see §6. +- No ambient switch exists on the cheatsheet (`motion-settings` is not ported), so + `prefers-reduced-motion` is the only motion gate. This satisfies DESIGN.md's "all motion gated on + `prefers-reduced-motion`". + +--- + +## 2. Why both fields, rather than a replacement + +The banner plate is already painted by the fuzz field (`SectionBanner.astro:79-290`): six +pixel-sorted streak bands plus 54 bright filaments on a seamless 9-second loop, composited +`lighter` on dark at `--fuzz-gain: 1`. + +Layered naively the two fight, and the arithmetic says so. Fuzz streak alpha reaches +`(0.06 + 0.34) × env × hx × 1.7 × gain` — around 0.6 at peak, *additive* — while the constellation +draws its dots at `hostA * 0.38` and its resting links at `linkA * 0.03`. The streaks would eat the +lattice, and the cursor lock, which is the piece's entire gesture, needs quiet ground to read +against. + +The fix is not to retire the fuzz field. DESIGN.md lists "`SectionBanner` canvas animation reading +`--fuzz-*` tokens" under **behaviour that must keep working (do not regress)**. Instead: +`--fuzz-gain` is already a CSS custom property the fuzz canvas re-reads at draw time +(`SectionBanner.astro:136`), so lowering it on `.banner` in CSS alone rebalances the two with no +JavaScript change and no edit to the fuzz code. The plate then reads as two depths — dots as the +substrate, streaks as interference drifting across them. + +A pointer-driven cross-fade was considered and rejected on a substantive point rather than cost: the +constellation has an idle wanderer (`IDLE_MS = 2800`) that walks the cursor on its own precisely so +touch screens still see hosts being locked. Gating the field on pointer-enter would suppress exactly +that behaviour. + +--- + +## 3. Module — `src/scripts/signal-field.ts` + +```ts +export function mountSignalField(canvas: HTMLCanvasElement): () => void +``` + +Returns its own teardown. Everything the React `useEffect` body did, minus React. + +`VERT` and `FRAG` are lifted **byte-identical** from `ambient-signal.tsx:75-340`. All four styles +stay in the fragment shader even though only style 2 ships, and `styleFromUrl()` (`?field=N`) comes +with them. Two reasons: the `st()` branches are uniform-constant, so they are coherent across every +fragment and cost effectively nothing on the GPU; and a byte-identical shader stays diffable against +the original when daemon-sec moves. Stripping three styles would save no measurable frame time and +would cost the port its provenance. + +Carried over unchanged from the original: + +- **DPR-1 backing store.** Lines are one pixel wide and fragment cost is per pixel. +- **No layout reads inside a frame.** The canvas box is measured by `ResizeObserver` into a `box` + record; pointer events are converted using that record, never a fresh `getBoundingClientRect`. +- **Pointer listened for on `window`**, not the canvas, so content in front keeps its hover. The + canvas itself takes `pointer-events: none`. +- **The 2800ms idle wanderer**, so touch screens see hosts being locked. +- **Eased cursor** at a 0.07 step per frame — weight, not lag. +- **`HELD_TIME = 31.0`** as the still-frame phase. + +Deliberately dropped: the `tone` prop and its `SOURCES` table (§4), the React `failed` state (§7), +and `AmbientField` (§7). + +--- + +## 4. Palette + +The field reads four colours off its own canvas with `getComputedStyle`, re-reading on +`daemonmodechange`, because a GPU uniform does not inherit a CSS variable. + +| Uniform | Token | Resolves to inside `.banner.plate` | +|---|---|---| +| `u_ink` | `--text` | `#e0def4` | +| `u_love` | `--love-soft` | `#eb6f92` | +| `u_foam` | `--foam-soft` | `#9ccfd8` | +| `u_iris` | `--iris-soft` | `#c4a7e7` | + +These are the **theme-relative** tokens, not the absolute `--color-night-*` set, and that is the +design decision this section exists to record. The banner carries `.plate`, which pins the night +palette for its whole subtree in both modes — so the cascade has already answered the question and +the canvas only has to ask the right element. Three consequences: + +- The `tone: "page" | "plate"` prop disappears. There is one tone here, and CSS decides it. +- No new tokens are needed. The three missing `--color-dawn-*-soft` variants stay missing. +- If the plate is ever retuned, or a banner is ever placed in a `.plate-dawn` scope, the field + follows without a code change. + +This is also exactly how the fuzz field already resolves its palette (`SectionBanner.astro:129-151` +and its comment), so the two canvases on the plate answer the colour question the same way. + +All four resolve to plain `#rrggbb`, which the original's `hexToRgb` requires — no `color-mix()` +values are involved. The fallbacks are the original's `FALLBACK` table, whose values are already +these exact night hexes. + +--- + +## 5. Mount — `SectionBanner.astro` + +Markup gains one element, as the **first** child so it sits at the bottom of the stack: + +```astro +<header class="banner plate bleed" style={`--acc: var(--${accent});`}> + <canvas class="banner__signal" data-signal aria-hidden="true"></canvas> <!-- NEW --> + <canvas class="banner__field" data-fuzz aria-hidden="true"></canvas> + <span class="banner__scrim" aria-hidden="true"></span> + <span class="grain grain--local" aria-hidden="true"></span> + <span class="scanlines" aria-hidden="true"></span> + <div class="wrap banner__body"> … </div> +``` + +Both canvases sit under the existing `.banner__scrim`, so the title's two-layer legibility story — +the fuzz canvas erasing its own top and bottom bands by `destination-out`, **and** the scrim +gradient over it — is untouched. The new field adds no third layer to that story and takes no part +in it. + +The component's existing `<style>` block gains: + +```css +/* The constellation is the substrate; the streaks are interference across + it. At full gain the additive streaks eat a lattice drawn at 0.38 alpha, + so the fuzz field steps back on the banner. Read at draw time by the + fuzz canvas, so this is the whole adjustment — no JS change. */ +.banner { --fuzz-gain: 0.45; } +.banner__signal { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; } +``` + +The `0.45` is a starting value to be confirmed against the rendered plate during implementation, in +both modes. It is the one number in this design arrived at by reasoning rather than by reading the +original. + +The component's `<script>` imports the module and wraps it in the lifecycle the fuzz script already +uses — `data-bound` guard, `astro:page-load` init, `astro:before-swap` teardown, plus the +`DOMContentLoaded` / `readyState` pair for the first load: + +```ts +import { mountSignalField } from '../scripts/signal-field'; + +function initSignal(): void { + document.querySelectorAll<HTMLCanvasElement>('[data-signal]:not([data-bound])').forEach((cv) => { + cv.dataset.bound = '1'; + const teardown = mountSignalField(cv); + document.addEventListener('astro:before-swap', teardown, { once: true }); + }); +} +``` + +Routes affected, via the seven pages that use `SectionBanner`: `/[category]`, `/payloads`, +`/internal`, `/tags`, `/tags/[tag]`, `/hacktricks`, `/404`. + +--- + +## 6. The clearing + +The `data-index-zone` mechanism — hosts inside a marked box pushed out through the nearest edge, +links and locks faded to nothing inside — is **kept in the module and left unused**. + +Nothing in the banner carries the attribute, so `u_zone` stays at its empty sentinel +`(0, 0, -1, -1)`, `zoneSd` returns `1e3` on its first line, and the keep-out costs one uniform and +one early return per fragment. + +It is kept rather than stripped for two reasons. It is load-bearing behaviour in the original, so +removing it would make the port no longer a port. And the home page already has a +`<section id="index" class="dsection" data-index>` that is the structural analogue of daemon-sec's +index rows — if the field is ever extended there (the "banners + home bands" option considered and +set aside during brainstorming), the clearing is what it will need, and it should not have to be +re-derived. + +In the banner itself the scrim already does this job, better and more cheaply. + +--- + +## 7. Motion, pausing and degradation + +**Reduced motion.** `prefers-reduced-motion: reduce` means the loop never starts. One frame is drawn +at `HELD_TIME = 31.0` — probes spread, hosts settled — and redrawn only on resize and on +`daemonmodechange`. A still frame has to be redrawn by hand on a mode flip, or the plate keeps the +old palette until something else resizes it. + +**Pausing.** The loop stops when the tab is hidden (`visibilitychange`) and when the banner is +scrolled out of view (`IntersectionObserver`), and restarts on either returning. Both route through +one `sync()`, as in the original. + +**Degradation.** No WebGL, a shader that will not compile, a program that will not link, or a +`webglcontextlost` event all end the same way: tear down and remove the canvas from the DOM. The +fuzz field is still painting the plate, so the banner degrades to exactly what ships today. + +This is the one place the port deliberately diverges in structure rather than only in idiom. The +original falls back to `ambient-field.tsx` (141 lines of CSS orbs, rings and drifting dots) because +on daemon-sec the field is the *only* thing on that ground and removing it would leave the hero +bare. Here the ground is already occupied. Porting a second fallback component to sit behind a +working animation would add 141 lines to render something nobody sees. + +**Context teardown.** The context is left to the GC rather than lost by hand, carrying over the +original's closing comment: a forced loss fires `webglcontextlost` *after* cleanup has run, and the +next mount of the same canvas would hear it as a real loss and degrade for no reason. This matters +more here than in the original, because view-transition navigation between two banner pages mounts +and unmounts in quick succession. + +--- + +## 8. Testing + +No test framework runs against components in this repo (`test/` holds content checks), so +verification is by inspection, and the spec names what to inspect rather than pretending otherwise. + +Type checking: `npx tsc --noEmit`. Permitted by DESIGN.md; `npm run build` is **not**, while other +agents share the working copy. + +Checked by hand on a category page, in both modes: + +1. Dots visible on the plate at rest; the lattice reads *through* the fuzz streaks rather than + under them. Tune `--fuzz-gain` here if not. +2. Pointer across the banner lights links and locks hosts to love; the field settles when it leaves. +3. Banner title legibility unchanged against `main` — the failure mode to look for is the title on a + bright filament, which is what the two-layer scrim exists to prevent. +4. Theme toggle repaints the field rather than leaving it on the previous palette, both while + animating and while held still under reduced motion. +5. Reduced motion (`Emulate CSS prefers-reduced-motion` in devtools) shows a still frame, not a + blank plate, and that frame repaints on a theme toggle. +6. Navigating between two banner pages under view transitions leaves exactly one rAF loop running — + no accumulation, no context-loss fallback triggered by the swap. +7. Scrolling the banner out of view and back, and backgrounding the tab, stop and restart the loop. +8. Touch: the idle wanderer walks the cursor and hosts lock without a pointer. +9. WebGL disabled (`chrome://flags`, or forcing the context to fail): the canvas is removed and the + banner looks like `main`. + +--- + +## 9. Files + +| File | Change | +|---|---| +| `src/scripts/signal-field.ts` | **New.** Shader, uniform wiring, lifecycle, `mountSignalField`. | +| `src/components/SectionBanner.astro` | Canvas element, two CSS rules, an import and the init block. | + +No token changes. No new dependencies. No edits to `src/content/`, `scripts/`, the manifests or +`astro.config.mjs`.