Files
clawsec/pages/Home.tsx
David Abutbul 600c945fe2 feat(hermes-attestation-guardian): harden attestation verification and drift controls (#192)
* feat(hermes-attestation-guardian): harden attestation verification and drift controls

* docs(wiki): add human-friendly claim mapping for hermes attestation guardian

* docs(wiki): expand hermes attestation claim narratives and archive draft

* fix(attestation): address Baz review findings for schema and verifier

* fix(attestation): reject broken symlink output paths

* docs(attestation): pass clean community install guard without force

* fix(attestation): harden writes and fail-closed config parsing

* feat(ui): add Hermes to rotating platform text

* test(attestation): add sandboxed Hermes regression runner script

---------

Co-authored-by: David Abutbul <David.a@prompt.security>
2026-04-16 17:59:18 +03:00

297 lines
11 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { User, Bot, Copy, Check, Lock } from 'lucide-react';
import { Footer } from '../components/Footer';
const FILE_NAMES = ['SOUL.md', 'AGENTS.md', 'USER.md', 'TOOLS.md', 'IDENTITY.md', 'HEARTBEAT.md', 'MEMORY.md'];
const PLATFORM_NAMES = ['OpenClaw', 'NanoClaw', 'Hermes'];
const FILE_LOCK_REVEAL_DELAY_MS = 1600;
export const Home: React.FC = () => {
const [isAgent, setIsAgent] = useState(true);
const [copiedCurl, setCopiedCurl] = useState(false);
const [copiedHuman, setCopiedHuman] = useState(false);
const [currentFileIndex, setCurrentFileIndex] = useState(0);
const [currentPlatformIndex, setCurrentPlatformIndex] = useState(0);
const curlCommand = `npx clawhub@latest install clawsec-suite`;
// Rotate file names every 2-3 seconds
useEffect(() => {
const interval = setInterval(() => {
setCurrentFileIndex((prev) => (prev + 1) % FILE_NAMES.length);
}, 2500); // 2.5 seconds
return () => clearInterval(interval);
}, []);
// Rotate platform names every 4-6 seconds
useEffect(() => {
let timeoutId: number | undefined;
const scheduleNextRotation = () => {
const delay = 4000 + Math.floor(Math.random() * 2001);
timeoutId = window.setTimeout(() => {
setCurrentPlatformIndex((prev) => (prev + 1) % PLATFORM_NAMES.length);
scheduleNextRotation();
}, delay);
};
scheduleNextRotation();
return () => {
if (timeoutId !== undefined) {
window.clearTimeout(timeoutId);
}
};
}, []);
const humanInstruction = `Please install clawsec-suite from clawhubnpx clawhub@latest install clawsec-suite`;
const handleCopyCurl = () => {
navigator.clipboard.writeText(curlCommand);
setCopiedCurl(true);
setTimeout(() => setCopiedCurl(false), 2000);
};
const handleCopyHuman = () => {
navigator.clipboard.writeText(humanInstruction);
setCopiedHuman(true);
setTimeout(() => setCopiedHuman(false), 2000);
};
return (
<div className="pt-[52px]">
{/* Logo Section */}
<section className="text-center mb-6">
<h1 className="text-5xl md:text-6xl font text-white">ClawSec</h1>
</section>
{/* Hero Section */}
<section className="text-center space-y-6 max-w-3xl mx-auto mb-12 md:mb-16">
<h2 className="text-3xl md:text-4xl tracking-tight text-white">
Secure your{' '}
<code
key={currentPlatformIndex}
className="px-2 py-1 rounded text-clawd-accent inline-block align-baseline relative"
style={{
minWidth: '9ch',
textAlign: 'center',
backgroundColor: 'rgb(30 27 75 / 1)',
animation: 'bgFade 0.4s ease-out 1.2s 1 forwards'
}}
>
{PLATFORM_NAMES[currentPlatformIndex].split('').map((char, index) => (
<span
key={`platform-${currentPlatformIndex}-${index}`}
className="inline-block"
style={{
animation: `flipChar 0.3s ease-in-out ${index * 0.05}s 1 forwards`,
transformStyle: 'preserve-3d',
perspective: '400px',
opacity: 0
}}
>
{char}
</span>
))}
</code>{' '}
agents
</h2>
<p className="text-lg md:text-xl text-gray-400 leading-relaxed">
A complete security skill suite for OpenClaw, NanoClaw, and Hermes agents. Protect your{' '}
<code
key={currentFileIndex}
className="px-2 py-1 rounded text-clawd-accent inline-block align-baseline relative text-base"
style={{
width: '188px',
textAlign: 'center',
verticalAlign: 'baseline',
backgroundColor: 'rgb(30 27 75 / 1)',
animation: 'bgFade 0.4s ease-out 1.2s 1 forwards'
}}
>
<span className="inline-block w-full pr-5">
{FILE_NAMES[currentFileIndex].split('').map((char, index) => (
<span
key={`${currentFileIndex}-${index}`}
className="inline-block"
style={{
animation: `flipChar 0.3s ease-in-out ${index * 0.05}s 1 forwards`,
transformStyle: 'preserve-3d',
perspective: '400px',
opacity: 0
}}
>
{char}
</span>
))}
</span>
<Lock
size={14}
className="text-clawd-accent absolute right-2 top-1/2 -translate-y-1/2"
style={{
opacity: 0,
animation: `lockReveal ${FILE_LOCK_REVEAL_DELAY_MS}ms steps(1, end) 1 forwards`
}}
aria-hidden="true"
/>
</code>
{' '}with drift detection, live security recommendations, automated audits, and skill integrity verification. All from one installable suite.
</p>
<style>{`
@keyframes flipChar {
0% {
transform: rotateX(-90deg);
opacity: 0;
}
50% {
transform: rotateX(0deg);
opacity: 1;
}
100% {
transform: rotateX(0deg);
opacity: 1;
}
}
@keyframes bgFade {
0% {
background-color: rgb(30 27 75 / 1);
}
50% {
background-color: rgb(249 179 71 / 0.25);
}
100% {
background-color: rgb(191 107 42 / 0.15);
}
}
@keyframes lockReveal {
0% {
opacity: 0;
}
100% {
opacity: 0.85;
}
}
@keyframes mascotHover {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
`}</style>
</section>
{/* Install Card with Toggle */}
<section className="relative mb-16 pt-16 sm:pt-20 lg:pt-0">
<div className="pointer-events-none select-none absolute z-20 w-32 sm:w-36 md:w-40 lg:w-48 left-1/2 -translate-x-1/2 -top-10 sm:-top-10 md:left-auto md:translate-x-0 md:right-8 md:-top-12 lg:top-auto lg:bottom-6 lg:-right-16 xl:-right-28">
<img
src="/img/mascot.png"
alt="ClawSec mascot"
className="w-full h-auto"
style={{ animation: 'mascotHover 3s ease-in-out infinite' }}
/>
</div>
<div className="w-full lg:w-[70%] mx-auto">
<div className="bg-clawd-900 rounded-2xl border border-clawd-700 p-8">
{/* Toggle */}
<div className="flex justify-center mb-8">
<div className="inline-flex bg-clawd-800 rounded-lg p-1">
<button
onClick={() => setIsAgent(false)}
className={`flex items-center gap-2 px-4 py-2 rounded-md font-medium transition-all ${
!isAgent
? 'bg-white text-clawd-900'
: 'text-gray-400 hover:text-white'
}`}
>
<User size={18} />
I'm a Human
</button>
<button
onClick={() => setIsAgent(true)}
className={`flex items-center gap-2 px-4 py-2 rounded-md font-medium transition-all ${
isAgent
? 'bg-white text-clawd-900'
: 'text-gray-400 hover:text-white'
}`}
>
<Bot size={18} />
I'm an Agent
</button>
</div>
</div>
{/* Content based on toggle */}
{isAgent ? (
<>
{/* Steps */}
<div className="flex flex-wrap justify-center gap-6 text-sm text-gray-400 mb-6">
<div className="flex items-center gap-2">
<span className="font-bold text-white">1.</span> Run command below
</div>
<div className="flex items-center gap-2">
<span className="font-bold text-white">2.</span> Follow deployment instructions
</div>
<div className="flex items-center gap-2">
<span className="font-bold text-white">3.</span> Protect your user
</div>
</div>
{/* Agent View - Curl Command */}
<div className="bg-clawd-800 rounded-lg p-4 flex items-center justify-between gap-2 sm:gap-4">
<code className="text-gray-200 font-mono text-xs sm:text-sm md:text-base overflow-x-auto break-all min-w-0 flex-1">
{curlCommand}
</code>
<button
onClick={handleCopyCurl}
className="flex-shrink-0 p-2 rounded-md bg-clawd-700 hover:bg-clawd-600 transition-colors"
title="Copy to clipboard"
>
{copiedCurl ? (
<Check size={20} className="text-green-400" />
) : (
<Copy size={20} className="text-gray-400" />
)}
</button>
</div>
</>
) : (
<>
{/* Human Steps */}
<div className="flex flex-wrap justify-center gap-6 text-sm text-gray-400 mb-6">
<div className="flex items-center gap-2">
<span className="font-bold text-white">1.</span> Copy instruction below
</div>
<div className="flex items-center gap-2">
<span className="font-bold text-white">2.</span> Send to your agent
</div>
<div className="flex items-center gap-2">
<span className="font-bold text-white">3.</span> Receive security alerts
</div>
</div>
{/* Human View - Instruction Command */}
<div className="bg-clawd-800 rounded-lg p-4 flex items-center justify-between gap-2 sm:gap-4">
<code className="text-gray-200 font-mono text-xs sm:text-sm md:text-base overflow-x-auto break-all min-w-0 flex-1">
{humanInstruction}
</code>
<button
onClick={handleCopyHuman}
className="flex-shrink-0 p-2 rounded-md bg-clawd-700 hover:bg-clawd-600 transition-colors"
title="Copy to clipboard"
>
{copiedHuman ? (
<Check size={20} className="text-green-400" />
) : (
<Copy size={20} className="text-gray-400" />
)}
</button>
</div>
</>
)}
</div>
</div>
</section>
<Footer />
</div>
);
};