diff --git a/.gitignore b/.gitignore index a60926c..a3bffa5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,20 @@ .DS_Store .env -.env.* +.env.local +.env.*.local +.env.development +.env.test +.env.production +.env.staging .venv __pycache__/ *.pyc dist/ build/ .pytest_cache/ +node_modules/ +.vite/ # Re-voice workspace tmp/ *.log - diff --git a/site/red-team-shadow-dossiers/.env.example b/site/red-team-shadow-dossiers/.env.example new file mode 100644 index 0000000..488da2b --- /dev/null +++ b/site/red-team-shadow-dossiers/.env.example @@ -0,0 +1,2 @@ +VITE_PUBLIC_BASE_URL= +VITE_ENABLE_ROAST=false diff --git a/site/red-team-shadow-dossiers/.gitignore b/site/red-team-shadow-dossiers/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/site/red-team-shadow-dossiers/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/site/red-team-shadow-dossiers/App.tsx b/site/red-team-shadow-dossiers/App.tsx new file mode 100644 index 0000000..96fa857 --- /dev/null +++ b/site/red-team-shadow-dossiers/App.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Header } from './components/Header'; +import { Hero } from './components/Hero'; +import { LeakViewer } from './components/LeakViewer'; +import { PricingTiers } from './components/PricingTiers'; +import { RoastGenerator } from './components/RoastGenerator'; +import { Footer } from './components/Footer'; + +const App: React.FC = () => { + return ( +
+
+
+ + + + + + {/* Evidence CTA */} +
+
+
+
+
Verification
+

Evidence index & trace verification

+

+ Browse hashes, trace bundles, and reproducibility notes. If you want the bite, keep the proof. +

+
+ + Evidence index (staged) + +
+
+
+
+
+ ); +}; + +export default App; diff --git a/site/red-team-shadow-dossiers/CODEX_IMPLEMENTATION.md b/site/red-team-shadow-dossiers/CODEX_IMPLEMENTATION.md new file mode 100644 index 0000000..6df5774 --- /dev/null +++ b/site/red-team-shadow-dossiers/CODEX_IMPLEMENTATION.md @@ -0,0 +1,75 @@ +# CODEX Implementation Guide (Server + Launch Wiring) + +This repo is a **pre-launch staging front-end**. It is intentionally safe: +- No “live” PDF/evidence links are active unless `dossier.status === 'live'` +- No model API keys are shipped to the browser +- The roast generator (if enabled) calls a server endpoint + +## 1) Publish static artifacts + +Target paths (recommended): +- PDF: `/static/hosted/snyk-shadow-dossier.pdf` +- Evidence index: `/static/hosted/evidence/index.html` + +### What to ship +- The free public PDF +- Evidence index HTML + any referenced assets (bundles, hashes, trace files) + +## 2) Flip dossiers to LIVE + +Edit `constants.tsx`: + +- Set: + - `status: 'live'` + - `pdfPath: '/static/hosted/.pdf'` + - `evidencePath: '/static/hosted/evidence/index.html'` + +Optional: +- Add `sha256` and `bytes` (use `node scripts/hash.mjs path/to.pdf`) +- Add `gumroadUrl` for classified edition +- Confirm `contactEmail` + +## 3) Configure base URL (optional) + +If you deploy under a domain/subdomain and want absolute links, set: + +- `VITE_PUBLIC_BASE_URL=https://infrafabric.io` (or your subdomain) + +If unset, the app uses relative links (works fine for same-origin hosting). + +## 4) Roast Generator (optional) + +### Current behavior +- UI is hidden unless: `VITE_ENABLE_ROAST=true` +- Client calls: `POST /api/roast` with JSON `{ "content": "..." }` +- Expected response: `{ "text": "..." }` + +### Implementing the endpoint +You can implement this in whichever server environment you use. Examples are in `server_stubs/`. + +Required behavior: +- Validate request body +- Rate limit (recommended) +- Call your LLM provider using a **server-side** secret +- Return JSON `{ text }` + +### Provider dependency +If you want Gemini, add `@google/genai` server-side and use `server_stubs/gemini_client_example.ts` as a starting point. + +## 5) Replace staged UI labels (optional) + +Staged labels are intentional pre-launch. +Once live, you can: +- Rename “Evidence (staged)” back to “Evidence” +- Replace “Request early access” with “Download PDF” +- Enable premium tier by adding Gumroad URL in `constants.tsx` + +## 6) Final checklist + +- [ ] PDF link works (no 404) +- [ ] Evidence index link works +- [ ] `status` set to `live` +- [ ] Optional: SHA-256 + bytes present and correct +- [ ] No secret keys shipped to client bundles +- [ ] OpenGraph image exists at `/og-dossier.jpg` +- [ ] Favicon present at `/favicon.svg` diff --git a/site/red-team-shadow-dossiers/README.md b/site/red-team-shadow-dossiers/README.md new file mode 100644 index 0000000..d6cb408 --- /dev/null +++ b/site/red-team-shadow-dossiers/README.md @@ -0,0 +1,64 @@ +# InfraFabric Red Team — Shadow Dossiers (Front-End) + +A minimalist React + Vite publication hub for InfraFabric’s “Shadow Dossiers” drops: +- serious, paper/ink layout +- staged/publication statuses (draft/scheduled/live) +- verifiability hooks (evidence index + optional SHA-256 + size) +- **no client-side model keys** (roast generator is feature-flagged and calls a server endpoint) + +## Local dev + +Prereqs: Node.js 18+ + +```bash +npm install +npm run dev +``` + +Open: http://localhost:3000 + +## Production (self-host) + +```bash +npm install +npm run build +PORT=8080 npm run start +``` + +## Environment variables + +Create `.env.local` from `.env.example`: + +- `VITE_PUBLIC_BASE_URL` + Optional. When set (e.g. `https://infrafabric.io`) it will prefix dossier `pdfPath`/`evidencePath`. + +- `VITE_ENABLE_ROAST` + Defaults to `false`. When `true`, renders the Roast Generator UI which calls `POST /api/roast`. + +## Staging behavior (important) + +This bundle is **pre-launch**. Dossier buttons are gated by `dossier.status`: + +- `draft` / `scheduled`: download + evidence links remain disabled, and the UI shows “staged.” +- `live`: links activate (and optional SHA-256/bytes render if populated). + +Update dossier status and paths in `constants.tsx`. + +## Codex/server implementation checklist + +See `CODEX_IMPLEMENTATION.md` for the complete checklist and stubs. + +Quick summary: +1. Publish PDFs and evidence pages at: + - `/static/hosted/.pdf` + - `/static/hosted/evidence/index.html` +2. Set `VITE_PUBLIC_BASE_URL` in the deployment environment if needed. +3. (Optional) Implement `POST /api/roast` server-side and set `VITE_ENABLE_ROAST=true`. + +## Hash helper + +Compute SHA-256 + bytes for a PDF and paste into the dossier entry: + +```bash +node scripts/hash.mjs /path/to/file.pdf +``` diff --git a/site/red-team-shadow-dossiers/components/Disclaimer.tsx b/site/red-team-shadow-dossiers/components/Disclaimer.tsx new file mode 100644 index 0000000..93a87ae --- /dev/null +++ b/site/red-team-shadow-dossiers/components/Disclaimer.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +export const Disclaimer: React.FC = () => ( +
+
+ Legal notice + + Satire / parody + +
+

+ This site contains satirical critical commentary. The “Shadow Dossiers” are performance art intended to highlight + systemic issues in governance, compliance, and security tooling. Nothing here is presented as a statement of fact + about any specific organization. Verification artifacts (hashes, traces, evidence index) are provided to support + reproducibility of the published materials. +

+
+); diff --git a/site/red-team-shadow-dossiers/components/Footer.tsx b/site/red-team-shadow-dossiers/components/Footer.tsx new file mode 100644 index 0000000..a3b894d --- /dev/null +++ b/site/red-team-shadow-dossiers/components/Footer.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +export const Footer: React.FC = () => { + return ( +
+
+
+
+
+
+ IF +
+
+
InfraFabric Red Team
+
Shadow Dossiers
+
+
+

+ Satirical critical commentary, published alongside verification artifacts (hashes, traces, evidence index). +

+
+ +
+
+
Links
+ +
+ +
+
Notice
+

+ This is satire/parody and critical commentary; not affiliated with any vendor. Nothing here is presented as a statement of fact. +

+ Read the disclaimer +
+
+
+ +
+

+ © 2025 InfraFabric. Shadow Dossiers. +

+
+
+
+ ); +}; diff --git a/site/red-team-shadow-dossiers/components/Header.tsx b/site/red-team-shadow-dossiers/components/Header.tsx new file mode 100644 index 0000000..7d797b2 --- /dev/null +++ b/site/red-team-shadow-dossiers/components/Header.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +export const Header: React.FC = () => { + return ( +
+
+
+
+ IF +
+
+
InfraFabric Red Team
+
Shadow Dossiers
+
+
+ + +
+
+ ); +}; diff --git a/site/red-team-shadow-dossiers/components/Hero.tsx b/site/red-team-shadow-dossiers/components/Hero.tsx new file mode 100644 index 0000000..5c42f48 --- /dev/null +++ b/site/red-team-shadow-dossiers/components/Hero.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Disclaimer } from './Disclaimer'; + +export const Hero: React.FC = () => { + return ( +
+
+
+
+
+ Publication · Red Team Commentary · Verifiable Artifacts +
+

+ Shadow Dossiers +

+

+ Satirical, operator‑grade commentary on governance and compliance theater—published alongside an evidence index + so readers can verify what’s being referenced. +

+ + +
+ +
+ +
+
+
+
+ ); +}; diff --git a/site/red-team-shadow-dossiers/components/LeakViewer.tsx b/site/red-team-shadow-dossiers/components/LeakViewer.tsx new file mode 100644 index 0000000..6abd34b --- /dev/null +++ b/site/red-team-shadow-dossiers/components/LeakViewer.tsx @@ -0,0 +1,169 @@ +import React from 'react'; +import { DOSSIERS } from '../constants'; +import { resolvePublicUrl } from '../lib/urls'; + +const formatBytes = (bytes?: number) => { + if (!bytes) return null; + const units = ['B', 'KB', 'MB', 'GB']; + let b = bytes; + let i = 0; + while (b >= 1024 && i < units.length - 1) { + b /= 1024; + i += 1; + } + return `${b.toFixed(i === 0 ? 0 : 1)} ${units[i]}`; +}; + +const statusLabel = (status: string) => { + switch (status) { + case 'live': + return 'LIVE'; + case 'scheduled': + return 'SCHEDULED'; + case 'draft': + default: + return 'DRAFT'; + } +}; + +export const LeakViewer: React.FC = () => { + return ( +
+
+
+
Dossiers
+

Current publications

+
+ +
+ {DOSSIERS.map((d) => { + const isLive = d.status === 'live'; + const pdfUrl = resolvePublicUrl(d.pdfPath); + const evidenceUrl = resolvePublicUrl(d.evidencePath); + + return ( +
+
+
+
+ {`Cover +
+
+ +
+
+ {d.leakDate} + + + + {d.classification} + + + + {statusLabel(d.status)} + + + + {d.id} +
+ +

+ {d.title} +

+ +

{d.summary}

+ +
+
+
Verification
+ + {d.sha256 && isLive ? ( +
+
SHA-256
+
{d.sha256}
+ {d.bytes ? ( +
Size: {formatBytes(d.bytes)}
+ ) : null} +
+ ) : ( +
+ Integrity details will publish with the drop. Until then, link verification remains staged. +
+ )} + + {evidenceUrl && isLive ? ( + + ) : null} +
+ +
+
Links
+ +
+ {pdfUrl && isLive ? ( + + Download PDF + + ) : ( + + PDF not published + + )} + + {d.gumroadUrl ? ( + + Classified edition + + ) : null} + + {d.contactEmail ? ( + + Contact + + ) : null} +
+ + {!isLive ? ( +
+ This dossier is staged. Links will activate when the drop is published. +
+ ) : null} +
+
+
+
+
+ ); + })} +
+
+
+ ); +}; diff --git a/site/red-team-shadow-dossiers/components/PricingTiers.tsx b/site/red-team-shadow-dossiers/components/PricingTiers.tsx new file mode 100644 index 0000000..796e59b --- /dev/null +++ b/site/red-team-shadow-dossiers/components/PricingTiers.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { PRICING_TIERS } from '../constants'; + +export const PricingTiers: React.FC = () => { + return ( +
+
+
+
Editions & services
+

Choose an engagement

+

+ The public drops are free. Paid editions fund ongoing work and include the working files. Custom dossiers are limited in capacity. +

+
+ +
+ {PRICING_TIERS.map((tier) => ( +
+
+
+ {tier.name} +
+
{tier.price}
+

+ {tier.description} +

+
+ +
    + {tier.features.map((f, i) => ( +
  • + + {f} +
  • + ))} +
+ {(() => { + const enabled = (tier.enabled !== false) && !!tier.actionUrl; + const isMailto = (tier.actionUrl || '').startsWith('mailto'); + + return ( +
+ + + {!enabled && tier.disabledHint ? ( +
{tier.disabledHint}
+ ) : null} +
+ ); + })()} + +
+ ))} +
+
+
+ ); +}; diff --git a/site/red-team-shadow-dossiers/components/RoastGenerator.tsx b/site/red-team-shadow-dossiers/components/RoastGenerator.tsx new file mode 100644 index 0000000..f6fefcd --- /dev/null +++ b/site/red-team-shadow-dossiers/components/RoastGenerator.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import { generateRoast } from '../services/roastClient'; +import { ENABLE_ROAST_GENERATOR } from '../lib/flags'; + +export const RoastGenerator: React.FC = () => { + if (!ENABLE_ROAST_GENERATOR) return null; + const [input, setInput] = useState(''); + const [output, setOutput] = useState(''); + const [loading, setLoading] = useState(false); + + const handleCritique = async () => { + if (!input.trim()) return; + setLoading(true); + try { + const result = await generateRoast(input); + setOutput(result); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
Quick critique
+

Preliminary “truth audit” (AI-assisted)

+

+ Paste a short excerpt from a policy, whitepaper, control description, or vendor claim. This produces a rough critique to help you + decide whether a custom dossier is worthwhile. Do not paste secrets. +

+
+ +
+
+ +