289 lines
9.0 KiB
TypeScript
289 lines
9.0 KiB
TypeScript
'use client'
|
|
|
|
import { ArrowRight, Check, X, Minus } from 'lucide-react'
|
|
|
|
type CellValue = 'yes' | 'no' | 'partial' | string
|
|
|
|
interface Row {
|
|
feature: string
|
|
greywall: CellValue
|
|
safehouse: CellValue
|
|
claudeSandbox: CellValue
|
|
containers: CellValue
|
|
}
|
|
|
|
const rows: Row[] = [
|
|
{
|
|
feature: 'Linux support',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'yes',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'macOS support',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'yes',
|
|
containers: 'partial',
|
|
},
|
|
{
|
|
feature: 'Filesystem isolation',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'yes',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'Network isolation',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'yes',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'Transparent network capture',
|
|
greywall: 'Linux',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'no',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'Command blocking',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'no',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'Real-time violation monitoring',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'no',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'Learning mode',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'no',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'Works with any agent',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'no',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'Sandboxes entire process',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'partial',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'Native tool access',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'yes',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'No image rebuilds on changes',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'yes',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'Open source',
|
|
greywall: 'yes',
|
|
safehouse: 'yes',
|
|
claudeSandbox: 'partial',
|
|
containers: 'yes',
|
|
},
|
|
{
|
|
feature: 'Syscall filtering',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'no',
|
|
containers: 'partial',
|
|
},
|
|
{
|
|
feature: 'Dynamic allow/deny',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'partial',
|
|
containers: 'no',
|
|
},
|
|
{
|
|
feature: 'No deprecated APIs',
|
|
greywall: 'yes',
|
|
safehouse: 'no',
|
|
claudeSandbox: 'yes',
|
|
containers: 'yes',
|
|
},
|
|
]
|
|
|
|
function CellIcon({ value }: { value: CellValue }) {
|
|
if (value === 'yes') {
|
|
return (
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10">
|
|
<Check className="h-3 w-3 text-green-400" />
|
|
</span>
|
|
)
|
|
}
|
|
if (value === 'no') {
|
|
return (
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10">
|
|
<X className="h-3 w-3 text-red-400/70" />
|
|
</span>
|
|
)
|
|
}
|
|
if (value === 'partial') {
|
|
return (
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10">
|
|
<Minus className="h-3 w-3 text-yellow-400/70" />
|
|
</span>
|
|
)
|
|
}
|
|
return (
|
|
<span className="text-xs font-mono text-muted-foreground">{value}</span>
|
|
)
|
|
}
|
|
|
|
export function Comparison() {
|
|
return (
|
|
<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.
|
|
</h2>
|
|
<p className="text-muted-foreground font-serif text-lg leading-relaxed">
|
|
Greywall combines filesystem isolation, network control, syscall filtering,
|
|
and real-time monitoring in a single tool. Here's how it stacks up.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Desktop table */}
|
|
<div className="hidden md:block rounded-lg border border-border/40 overflow-hidden">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="border-b border-border/40 bg-card/40">
|
|
<th className="text-left py-3.5 px-4 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
|
Feature
|
|
</th>
|
|
<th className="text-center py-3.5 px-3 font-sans font-semibold text-xs uppercase tracking-wider text-primary">
|
|
Greywall
|
|
</th>
|
|
<th className="text-center py-3.5 px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
|
Safehouse
|
|
</th>
|
|
<th className="text-center py-3.5 px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
|
Claude Sandbox
|
|
</th>
|
|
<th className="text-center py-3.5 px-3 font-sans font-medium text-muted-foreground text-xs uppercase tracking-wider">
|
|
Containers
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{rows.map((row, i) => (
|
|
<tr
|
|
key={row.feature}
|
|
className={`border-b border-border/20 ${i % 2 === 0 ? 'bg-card/10' : ''}`}
|
|
>
|
|
<td className="py-3 px-4 font-serif text-sm text-foreground">
|
|
{row.feature}
|
|
</td>
|
|
<td className="py-3 px-3 text-center">
|
|
<div className="flex justify-center">
|
|
<CellIcon value={row.greywall} />
|
|
</div>
|
|
</td>
|
|
<td className="py-3 px-3 text-center">
|
|
<div className="flex justify-center">
|
|
<CellIcon value={row.safehouse} />
|
|
</div>
|
|
</td>
|
|
<td className="py-3 px-3 text-center">
|
|
<div className="flex justify-center">
|
|
<CellIcon value={row.claudeSandbox} />
|
|
</div>
|
|
</td>
|
|
<td className="py-3 px-3 text-center">
|
|
<div className="flex justify-center">
|
|
<CellIcon value={row.containers} />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Mobile cards */}
|
|
<div className="md:hidden space-y-2">
|
|
{rows.map((row) => (
|
|
<div
|
|
key={row.feature}
|
|
className="p-4 rounded-lg border border-border/30 bg-card/20"
|
|
>
|
|
<div className="font-serif text-sm text-foreground mb-3">{row.feature}</div>
|
|
<div className="grid grid-cols-4 gap-1.5 text-center">
|
|
<div>
|
|
<div className="text-[10px] font-sans text-primary font-medium mb-1">Greywall</div>
|
|
<div className="flex justify-center"><CellIcon value={row.greywall} /></div>
|
|
</div>
|
|
<div>
|
|
<div className="text-[10px] font-sans text-muted-foreground font-medium mb-1">Safehouse</div>
|
|
<div className="flex justify-center"><CellIcon value={row.safehouse} /></div>
|
|
</div>
|
|
<div>
|
|
<div className="text-[10px] font-sans text-muted-foreground font-medium mb-1">Claude</div>
|
|
<div className="flex justify-center"><CellIcon value={row.claudeSandbox} /></div>
|
|
</div>
|
|
<div>
|
|
<div className="text-[10px] font-sans text-muted-foreground font-medium mb-1">Containers</div>
|
|
<div className="flex justify-center"><CellIcon value={row.containers} /></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Legend */}
|
|
<div className="mt-6 flex flex-wrap items-center gap-5 text-xs font-sans text-muted-foreground">
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-400/10">
|
|
<Check className="h-3 w-3 text-green-400" />
|
|
</span>
|
|
Supported
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-yellow-400/10">
|
|
<Minus className="h-3 w-3 text-yellow-400/70" />
|
|
</span>
|
|
Partial
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-red-400/10">
|
|
<X className="h-3 w-3 text-red-400/70" />
|
|
</span>
|
|
Not supported
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|