From 923fefc6aab5b9a0b8e6e86363986f444d4faa56 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 01:46:54 +0000 Subject: [PATCH 1/3] Surface Trust Score tiers in marketplace browse and ranking - Batched package_trust_scores join in the cached marketplace list (fail-soft) - Tier chips on cards (Verified/High/Baseline/Low/Unscored) using real score.ts thresholds - Default sort demotes unscored/below-baseline packages; new Trust score sort option Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01SqR7Sa8zNjf5MDDeg5f7vc --- src/lib/marketplace/list.functions.ts | 28 ++++++++++++++ src/lib/marketplace/trust-tiers.ts | 48 +++++++++++++++++++++++ src/routes/marketplace.index.tsx | 56 ++++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 src/lib/marketplace/trust-tiers.ts diff --git a/src/lib/marketplace/list.functions.ts b/src/lib/marketplace/list.functions.ts index 5d560074..bec8d2fb 100644 --- a/src/lib/marketplace/list.functions.ts +++ b/src/lib/marketplace/list.functions.ts @@ -22,6 +22,10 @@ export type MarketplaceItem = { rating_human_avg: number; rating_agent_count: number; rating_agent_avg: number; + /** Trust Score 0..1 from package_trust_scores; null when never scored. */ + trust_score: number | null; + /** Evidence-gated verification flag from package_trust_scores. */ + trust_verified: boolean; }; type ListPayload = { items: MarketplaceItem[]; verticals: string[] }; @@ -92,11 +96,33 @@ async function fetchMarketplace(): Promise { } } + // Trust scores per package (single batched query, keyed by package id). + // Fail soft: on any error every package renders as "Unscored". + const trustByPkg = new Map(); + if (ids.length) { + try { + const { data: ts } = await supabaseAdmin + .from("package_trust_scores") + .select("package_id, score, verified") + .in("package_id", ids); + for (const t of ts ?? []) { + const score = t.score == null ? null : Number(t.score); + trustByPkg.set(t.package_id, { + score: score != null && Number.isFinite(score) ? score : null, + verified: Boolean(t.verified), + }); + } + } catch { + /* trust data unavailable → leave map empty */ + } + } + const round2 = (n: number) => Math.round(n * 100) / 100; const items: MarketplaceItem[] = (pkgs ?? []).map((p) => { const rt = ratingByPkg.get(p.id) ?? { sum: 0, count: 0, humanSum: 0, humanCount: 0, agentSum: 0, agentCount: 0 }; + const trust = trustByPkg.get(p.id); return { ...p, type: p.type as MarketplaceItem["type"], @@ -108,6 +134,8 @@ async function fetchMarketplace(): Promise { rating_human_avg: rt.humanCount ? round2(rt.humanSum / rt.humanCount) : 0, rating_agent_count: rt.agentCount, rating_agent_avg: rt.agentCount ? round2(rt.agentSum / rt.agentCount) : 0, + trust_score: trust?.score ?? null, + trust_verified: trust?.verified ?? false, }; }); diff --git a/src/lib/marketplace/trust-tiers.ts b/src/lib/marketplace/trust-tiers.ts new file mode 100644 index 00000000..030e7682 --- /dev/null +++ b/src/lib/marketplace/trust-tiers.ts @@ -0,0 +1,48 @@ +// Trust tiers for catalog curation. +// +// Maps a package's Trust Score (0..1, from package_trust_scores) plus its +// evidence-gated `verified` flag into a small set of browse-facing tiers. +// Thresholds are NOT invented here: they reuse the public badgeColor() bands +// defined in @/lib/trust/score (green >= 0.85, yellow >= 0.70, orange >= 0.50) +// and the verification gate computed server-side by recompute_trust_scores_v2 +// (MIN_ADVERSARIAL_RUNS_FOR_VERIFIED / MIN_ADVERSARIAL_CASES_FOR_VERIFIED in +// @/lib/trust/scoring). Pure module — safe to import from client code. + +import { badgeColor } from "@/lib/trust/score"; + +export type TrustTier = "verified" | "high" | "baseline" | "low" | "unscored"; + +export function trustTier(score: number | null | undefined, verified: boolean): TrustTier { + if (score == null || Number.isNaN(score)) return "unscored"; + if (verified) return "verified"; + const color = badgeColor(score); + if (color === "green" || color === "yellow") return "high"; // >= 0.70 + if (color === "orange") return "baseline"; // >= 0.50 + return "low"; // < 0.50 +} + +export const TRUST_TIER_LABELS: Record = { + verified: "Verified", + high: "High trust", + baseline: "Baseline", + low: "Low trust", + unscored: "Unscored", +}; + +// Lower = better. Used by the "Trust score" sort and by ranking demotion. +export const TRUST_TIER_RANK: Record = { + verified: 0, + high: 1, + baseline: 2, + low: 3, + unscored: 4, +}; + +/** + * True when a package should be demoted in the default browse sort: + * unscored, or scored below the Baseline band (score < 0.50). + * Nothing is hidden — demoted packages simply sort after scored ones. + */ +export function isTrustDemoted(tier: TrustTier): boolean { + return tier === "low" || tier === "unscored"; +} diff --git a/src/routes/marketplace.index.tsx b/src/routes/marketplace.index.tsx index e2f2dee3..760d9fd1 100644 --- a/src/routes/marketplace.index.tsx +++ b/src/routes/marketplace.index.tsx @@ -6,6 +6,13 @@ import { Nav } from "@/components/site/Nav"; import { Footer } from "@/components/site/Footer"; import { TermsStatusBanner } from "@/components/site/TermsStatusBanner"; import { listMarketplace, type MarketplaceItem } from "@/lib/marketplace/list.functions"; +import { + isTrustDemoted, + TRUST_TIER_LABELS, + TRUST_TIER_RANK, + trustTier, + type TrustTier, +} from "@/lib/marketplace/trust-tiers"; import { BuyButton, PriceBadge } from "@/components/marketplace/BuyButton"; import { Stars } from "@/components/reviews/Stars"; import { ShareOnXButton } from "@/components/share/ShareOnXButton"; @@ -16,6 +23,7 @@ import { Bot, FolderOpen, Palette, + ShieldCheck, ShoppingBag, Smartphone, Sparkles, @@ -62,6 +70,7 @@ const SORTS = [ { value: "installs_desc", label: "Most installed" }, { value: "installs_asc", label: "Least installed" }, { value: "rating_desc", label: "Top rated" }, + { value: "trust_desc", label: "Trust score" }, { value: "recent", label: "Newest" }, { value: "name_asc", label: "Name A→Z" }, ] as const; @@ -142,8 +151,22 @@ function Marketplace() { const sorted = [...result]; sorted.sort((a, b) => { switch (sort) { - case "installs_desc": + case "installs_desc": { + // Default sort only: curation demotion. Unscored / below-Baseline + // packages sort after trust-scored ones (nothing is hidden); order + // within each group stays install-count based. + const demoteA = isTrustDemoted(trustTier(a.trust_score, a.trust_verified)) ? 1 : 0; + const demoteB = isTrustDemoted(trustTier(b.trust_score, b.trust_verified)) ? 1 : 0; + if (demoteA !== demoteB) return demoteA - demoteB; return b.install_count - a.install_count; + } + case "trust_desc": { + const tierA = trustTier(a.trust_score, a.trust_verified); + const tierB = trustTier(b.trust_score, b.trust_verified); + if (TRUST_TIER_RANK[tierA] !== TRUST_TIER_RANK[tierB]) + return TRUST_TIER_RANK[tierA] - TRUST_TIER_RANK[tierB]; + return (b.trust_score ?? -1) - (a.trust_score ?? -1) || b.install_count - a.install_count; + } case "installs_asc": return a.install_count - b.install_count; case "rating_desc": @@ -435,7 +458,10 @@ function Card({ p }: { p: MarketplaceItem }) { className="group relative flex flex-col rounded-xl border border-border bg-background p-5 transition-all focus-within:border-primary/40 hover:border-primary/40 hover:shadow-elevated" >
- +
+ + +
{p.type !== "soul" && } v{p.latest_version} @@ -522,6 +548,32 @@ function Card({ p }: { p: MarketplaceItem }) { ); } +function TrustTierChip({ tier, score }: { tier: TrustTier; score: number | null }) { + const cls: Record = { + verified: "border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400", + high: "border-sky-500/30 bg-sky-500/10 text-sky-700 dark:text-sky-400", + baseline: "border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-400", + low: "border-border bg-muted/40 text-muted-foreground", + unscored: "border-border bg-muted/40 text-muted-foreground", + }; + const title = + score != null + ? `Trust score ${(score * 100).toFixed(0)}/100${tier === "verified" ? " · adversarially verified" : ""}` + : "No trust score yet — not adversarially tested"; + return ( + + + {TRUST_TIER_LABELS[tier]} + + {score != null ? ` trust tier, score ${(score * 100).toFixed(0)} out of 100` : " trust tier"} + + + ); +} + function CategoryChip({ active, onClick, From 882dff3f8dca9ca4359d98cbf139f966794d849d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 01:49:31 +0000 Subject: [PATCH 2/3] Add 8 editorial SEO comparison pages and /compare index - Curated comparisons (vs GitHub prompt repos, in-house, fine-tuning, RAG, LangChain Hub, custom GPTs, unsigned prompts, untested marketplaces) grounded in real product facts - compare.$pair loader: editorial-first with DB pair fallback; FAQPage + Article JSON-LD, canonicals, CTAs - /compare index with CollectionPage JSON-LD; all URLs added to sitemap Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01SqR7Sa8zNjf5MDDeg5f7vc --- src/lib/seo/comparisons.ts | 424 +++++++++++++++++++++++++++++++++++ src/routeTree.gen.ts | 63 ++++++ src/routes/compare.$pair.tsx | 137 ++++++++++- src/routes/compare.index.tsx | 70 ++++++ src/routes/sitemap[.]xml.tsx | 12 +- 5 files changed, 696 insertions(+), 10 deletions(-) create mode 100644 src/lib/seo/comparisons.ts create mode 100644 src/routes/compare.index.tsx diff --git a/src/lib/seo/comparisons.ts b/src/lib/seo/comparisons.ts new file mode 100644 index 00000000..cd52d3f1 --- /dev/null +++ b/src/lib/seo/comparisons.ts @@ -0,0 +1,424 @@ +// Curated editorial comparison pages for /compare/. +// These own high-intent "X vs Y" queries. Each entry renders a full SSR page +// with keyword title, meta description, FAQPage JSON-LD, and marketplace CTAs. +// +// All claims here are grounded in the product: Ed25519 signing, adversarial +// testing with attacker/judge LLMs, the public Trust Score formula +// (adversarial 45%, real-world success 20%, signing 10%, age 15%, schema 10%), +// SkillForge re-testing, offline verification (npm run trust:verify), +// MCP one-URL install, and the npx CLI. No invented numbers or customers. + +export interface ComparisonSection { + heading: string; + paragraphs: string[]; +} + +export interface ComparisonFaq { + question: string; + answer: string; +} + +export interface Comparison { + slug: string; // URL: /compare/ + title: string; // H1 + keyword phrase + metaDescription: string; + lede: string; + sections: ComparisonSection[]; + faqs: ComparisonFaq[]; + verdict: string; +} + +const TRUST_FORMULA = + "The Trust Score is a public, reproducible formula: adversarial test results count for 45%, real-world success telemetry for 20%, signing status for 10%, package age for 15%, and schema validity for 10%. Nothing is editorially assigned, and anyone can recompute a score offline with npm run trust:verify."; + +export const COMPARISONS: Comparison[] = [ + { + slug: "super-agent-skill-vs-github-prompt-repos", + title: "Super Agent Skill vs raw GitHub prompt repos", + metaDescription: + "Should you copy prompts from GitHub repos or install verified skills? Compare raw prompt repos with Super Agent Skill's signed, adversarially tested marketplace.", + lede: + "GitHub is full of awesome-prompts repos with tens of thousands of stars. They are a great starting point for inspiration — and a risky foundation for production agents. Here is how a raw prompt repo compares to a verified skill marketplace, point by point.", + sections: [ + { + heading: "Provenance: stars vs signatures", + paragraphs: [ + "A GitHub star tells you a repo was popular at some moment. It tells you nothing about who last edited the prompt you are about to paste into an agent with tool access. Repos change hands, maintainers accept drive-by PRs, and a prompt that was safe six months ago can be quietly modified — there is no mechanism that binds the text you copy to any identity or review event.", + "Every skill on Super Agent Skill is signed with Ed25519. The signature binds the exact content to the author's key, so a modified skill fails verification instead of silently shipping. You can check signatures offline with npm run trust:verify — no need to trust our servers, or anyone's star count.", + ], + }, + { + heading: "Testing: 'works on my machine' vs adversarial evaluation", + paragraphs: [ + "Prompt repos are tested the way gists are tested: someone tried it once and it seemed to work. There is no harness that asks what happens when a hostile input hits the prompt — injection attempts, jailbreak framing, data-exfiltration bait buried in the context.", + "Skills in the marketplace go through adversarial testing where an attacker LLM actively tries to break the skill and a judge LLM scores the outcomes. The adversarial pass rate is published on every listing, and it is the single largest input to the Trust Score at 45% of the formula. SkillForge re-runs these tests over time, so a skill that regresses gets caught rather than coasting on an old result.", + ], + }, + { + heading: "Maintenance and install experience", + paragraphs: [ + "Copy-pasting from a repo means you own drift forever: when the upstream prompt improves, your pasted copy does not. When it breaks against a new model version, nobody tells you.", + "Skills install through MCP with a single URL, or via the npx CLI, and updates flow through the same channel with signatures verified on the way in. " + TRUST_FORMULA, + ], + }, + ], + faqs: [ + { + question: "Are GitHub prompt repos bad?", + answer: + "No — they are excellent for learning and prototyping. The problem is production use: no signing, no adversarial testing, no update channel, and no way to verify that the text you copied matches what the author intended.", + }, + { + question: "Can I bring a prompt from GitHub into Super Agent Skill?", + answer: + "Yes. Package it as a skill, sign it, and submit it. It will go through the same adversarial testing as everything else, and it earns a Trust Score from the public formula rather than from reputation.", + }, + { + question: "How do I verify a skill without trusting the marketplace?", + answer: + "Run npm run trust:verify locally. Ed25519 signature verification and Trust Score recomputation both work offline against the published formula.", + }, + ], + verdict: + "Use prompt repos to explore ideas. When an agent gets tool access, credentials, or customer data, switch to skills that are signed, adversarially tested, and continuously re-verified.", + }, + { + slug: "super-agent-skill-vs-building-in-house", + title: "Super Agent Skill vs building skills in-house", + metaDescription: + "Build agent skills in-house or install verified ones? Compare engineering cost, adversarial testing burden, and maintenance against Super Agent Skill's marketplace.", + lede: + "Your team can absolutely write its own agent skills — the question is whether writing the skill is the expensive part. In practice, the prompt is 20% of the work; the testing, hardening, and ongoing re-verification are the other 80%. Here is the honest build-vs-install breakdown.", + sections: [ + { + heading: "What 'building it in-house' actually includes", + paragraphs: [ + "Writing a skill that works on the happy path takes an afternoon. Making it safe takes an evaluation harness: adversarial test cases, injection suites, a way to score outputs, and a schedule to re-run all of it every time the underlying model or the skill changes. Most teams that start with 'we'll just write the prompt ourselves' end up building a miniature testing platform on the side — or skipping the testing and hoping.", + "There is also the provenance problem inside your own walls. Without signing, any engineer (or any compromised CI job) can edit the prompt an agent runs in production, and nothing flags it.", + ], + }, + { + heading: "What the marketplace amortizes for you", + paragraphs: [ + "Super Agent Skill runs adversarial testing with attacker and judge LLMs against every published skill, and SkillForge re-tests over time so results stay current instead of decaying. Skills are Ed25519-signed, so what you install is provably what the author published. " + TRUST_FORMULA, + "Installation is one MCP URL or an npx command, which means evaluating three candidate skills costs minutes, not sprints. The build-vs-buy math shifts: in-house makes sense for skills encoding genuinely proprietary logic; for everything generic — code review, redaction, triage, compliance checks — you are rebuilding tested commodities.", + ], + }, + { + heading: "The hybrid most teams land on", + paragraphs: [ + "Install verified skills for commodity capabilities, and reserve in-house effort for the skills that encode your domain edge. You can also publish in-house skills to the marketplace privately or publicly and inherit the testing infrastructure — the adversarial harness and re-testing pipeline run the same either way, which is usually cheaper than maintaining your own.", + ], + }, + ], + faqs: [ + { + question: "When does building in-house clearly win?", + answer: + "When the skill encodes proprietary business logic, touches internal systems no marketplace skill knows about, or when policy requires all agent instructions to be authored internally. Even then, you still need adversarial testing — the threat model doesn't care who wrote the prompt.", + }, + { + question: "Can we run marketplace-grade testing on our own skills?", + answer: + "Yes — submit them through SkillForge and they go through the same attacker/judge LLM evaluation and periodic re-testing as public skills.", + }, + { + question: "How do we audit what we install?", + answer: + "Every skill's Trust Score decomposes into its published components (adversarial 45%, real success 20%, signing 10%, age 15%, schema 10%), and npm run trust:verify recomputes it offline. Your security team can verify without trusting us.", + }, + ], + verdict: + "Build the skills that are your moat. Install the ones that aren't — and let the adversarial testing pipeline be someone else's infrastructure bill.", + }, + { + slug: "super-agent-skill-vs-untested-marketplace-prompts", + title: "Super Agent Skill vs untested marketplace prompts", + metaDescription: + "Not all prompt marketplaces test what they sell. See how Super Agent Skill's adversarial testing, Ed25519 signing, and public Trust Score compare to untested prompt stores.", + lede: + "Prompt marketplaces have multiplied, but most are storefronts: listings, ratings, a buy button. Ratings measure whether buyers liked the output on a sunny day — not whether the prompt survives a hostile one. Here is what separates a tested skill registry from an untested prompt store.", + sections: [ + { + heading: "Ratings are not security reviews", + paragraphs: [ + "A five-star prompt can still be trivially injectable. Reviews are written by users running friendly inputs; attackers do not leave reviews. Marketplaces that rank purely on popularity systematically overweight demo quality and underweight robustness, because robustness is invisible until it fails.", + "Super Agent Skill publishes an adversarial pass rate for every skill, produced by an attacker LLM that actively attempts injection, exfiltration, and instruction-override attacks, scored by a judge LLM. That number is 45% of the Trust Score — the largest single component — precisely because it is the thing star ratings cannot see.", + ], + }, + { + heading: "Tamper-evidence and re-testing", + paragraphs: [ + "On an untested marketplace, the prompt you download today may differ from the one that earned its reviews, and nothing will tell you. Skills here are Ed25519-signed: content is cryptographically bound to the author, and any modification breaks verification. You can confirm this yourself, offline, with npm run trust:verify.", + "Testing is also not a one-time stamp. SkillForge re-runs adversarial evaluations over time, so scores reflect current behavior against current models. " + TRUST_FORMULA, + ], + }, + { + heading: "What this means for buyers", + paragraphs: [ + "Instead of guessing from screenshots and testimonials, you compare skills on published, recomputable numbers: adversarial pass rate, real-world success telemetry, signing status, age, and schema validity. Install is a single MCP URL or an npx command, so trying the top two candidates side by side takes minutes.", + ], + }, + ], + faqs: [ + { + question: "What does 'adversarial testing' actually involve?", + answer: + "An attacker LLM generates hostile inputs — prompt injection, jailbreak framing, data-exfiltration attempts — against the skill, and a judge LLM scores whether the skill held its constraints. The pass rate is published on the listing.", + }, + { + question: "Can a skill's score go down after publication?", + answer: + "Yes. SkillForge re-tests skills over time, and the Trust Score reflects the latest results rather than the launch-day snapshot.", + }, + { + question: "Why should I trust the Trust Score?", + answer: + "You don't have to take it on faith. The formula is public (adversarial 45%, real success 20%, signing 10%, age 15%, schema 10%) and npm run trust:verify recomputes it offline from the underlying data.", + }, + ], + verdict: + "If a marketplace can't show you an adversarial pass rate and a signature, it is selling text files with a checkout flow. Demand testing you can verify.", + }, + { + slug: "signed-skills-vs-unsigned-prompts", + title: "Signed skills vs unsigned prompts: why it matters", + metaDescription: + "Unsigned prompts are unauditable inputs to systems with real permissions. Learn how Ed25519-signed skills give agents tamper-evident, offline-verifiable instructions.", + lede: + "Agents execute instructions with real permissions — repos, databases, payment APIs. An unsigned prompt is an unauthenticated instruction stream into that permission set. Code signing solved this problem for software distribution decades ago; skills are simply catching up.", + sections: [ + { + heading: "The threat model for unsigned prompts", + paragraphs: [ + "If a prompt lives in a wiki page, a shared doc, or an unsigned marketplace download, then anyone with write access to that surface can change what your agent does. The change is invisible: prompts are prose, diffs don't get security review, and the agent will cheerfully follow the new instructions. Supply-chain attacks on prompts don't need to break your infrastructure — they only need to edit a text file upstream of it.", + ], + }, + { + heading: "What Ed25519 signing gives you", + paragraphs: [ + "Every skill on Super Agent Skill is signed with Ed25519. The signature cryptographically binds the exact skill content to the author's key: if a single character changes, verification fails. That converts 'we assume nobody touched it' into 'we can prove nobody touched it.'", + "Verification is offline and independent: npm run trust:verify checks signatures and recomputes Trust Scores on your own machine, so your security posture never depends on trusting the marketplace's word. Signing status is also a component of the public Trust Score itself — 10% of the formula, alongside adversarial testing at 45%, real-world success at 20%, age at 15%, and schema validity at 10%.", + ], + }, + { + heading: "Signing plus testing, not signing instead of testing", + paragraphs: [ + "A signature proves who wrote a skill and that it hasn't changed — it does not prove the skill is safe. That is why signing is combined with adversarial testing by attacker and judge LLMs, and why SkillForge re-tests skills as models evolve. Signing gives you integrity and provenance; adversarial evaluation gives you robustness. Production agents need both.", + ], + }, + ], + faqs: [ + { + question: "What is Ed25519 and why use it for skills?", + answer: + "Ed25519 is a modern, widely audited elliptic-curve signature scheme — fast to verify, small keys and signatures, and standard in software supply-chain security. It makes each skill tamper-evident and attributable to its author's key.", + }, + { + question: "Does a valid signature mean a skill is safe?", + answer: + "No. It means the content is exactly what the author published. Safety comes from the adversarial testing layer, which is why the Trust Score weights adversarial results at 45% and signing at 10%.", + }, + { + question: "Can I verify signatures without an internet connection?", + answer: + "Yes — npm run trust:verify performs signature verification and Trust Score recomputation offline.", + }, + ], + verdict: + "You wouldn't run unsigned binaries in production. Prompts that drive agents with real permissions deserve the same bar — signed, verifiable, and tested.", + }, + { + slug: "mcp-skills-vs-fine-tuning", + title: "MCP skills vs fine-tuning: which should you use?", + metaDescription: + "Fine-tuning bakes behavior into weights; MCP skills deliver it as installable, signed, testable instructions. Compare cost, iteration speed, auditability, and portability.", + lede: + "Both fine-tuning and skills change how a model behaves. Fine-tuning does it by adjusting weights; a skill does it by supplying structured, installable instructions at runtime through MCP. The difference determines your iteration speed, your audit story, and your bill.", + sections: [ + { + heading: "Iteration speed and cost", + paragraphs: [ + "A fine-tune is a training job: assemble a dataset, train, evaluate, deploy, and repeat the whole loop for every change. When the base model updates, you retrain. A skill is a document: editing it, testing it, and shipping it happens in minutes, and installing one is a single MCP URL or an npx command.", + "For behavior that changes frequently — policies, workflows, tool-use patterns, domain checklists — the retraining loop is a tax on every iteration. Fine-tuning earns its cost when you need to shift the model's fundamental style or capabilities at a level instructions cannot reach, at high volume where per-request instruction tokens matter.", + ], + }, + { + heading: "Auditability and portability", + paragraphs: [ + "A fine-tuned model is opaque: you cannot diff two checkpoints and see what behavioral change shipped, and you cannot cryptographically attribute a behavior to an author. A skill is inspectable text with an Ed25519 signature binding it to its author — reviewable in a pull request, verifiable offline with npm run trust:verify.", + "Skills are also portable across the MCP ecosystem and across model upgrades. A fine-tune is welded to one base model; when the base improves, your investment depreciates. Skills ride the improvement for free, and SkillForge re-tests them against current models so you know they still hold.", + ], + }, + { + heading: "Safety evaluation", + paragraphs: [ + "Evaluating a fine-tune's safety means building your own red-team harness. Skills on Super Agent Skill arrive with adversarial testing already run — attacker LLM, judge LLM, published pass rate — and a Trust Score from a public formula (adversarial 45%, real success 20%, signing 10%, age 15%, schema 10%). That evaluation layer exists before you install, not after you deploy.", + ], + }, + ], + faqs: [ + { + question: "When is fine-tuning the right call?", + answer: + "When you need behavior that instructions can't reliably produce — deep style transfer, narrow-domain generation at very high volume, or latency/token budgets that rule out instruction overhead. For encoding procedures, policies, and tool workflows, skills are faster and cheaper to iterate.", + }, + { + question: "Can skills and fine-tuning be combined?", + answer: + "Yes. A common pattern is a lightly tuned (or stock) base model plus installable skills for task-specific behavior, keeping the auditable, fast-iterating layer in skills.", + }, + { + question: "Do skills survive model upgrades?", + answer: + "Skills are model-independent instructions, and SkillForge re-tests them over time, so regressions against new models are detected rather than assumed away. A fine-tune must be redone per base model.", + }, + ], + verdict: + "Fine-tune to change what a model is. Install skills to change what it does — with signatures, adversarial test results, and an offline-verifiable Trust Score attached.", + }, + { + slug: "mcp-skills-vs-rag", + title: "MCP skills vs RAG for domain expertise", + metaDescription: + "RAG retrieves knowledge; MCP skills encode procedure. Learn when to use retrieval, when to install a tested skill, and how the two combine for domain-expert agents.", + lede: + "RAG and skills both make an agent 'know' your domain, but they carry different cargo. RAG delivers facts at query time; a skill delivers procedure — how to reason, what to check, which tools to call, what to refuse. Most production agents need both, in different places.", + sections: [ + { + heading: "Knowledge vs procedure", + paragraphs: [ + "RAG excels when the answer lives in a corpus: product docs, tickets, contracts, research. Its failure mode is procedural — retrieval can hand a model the right document and still get a wrong process, because 'how to conduct a compliance review' is not a paragraph to retrieve, it's a discipline to follow.", + "A skill encodes that discipline directly: steps, constraints, output schemas, tool-use order, refusal conditions. It behaves the same on every run instead of depending on which chunks the retriever surfaced today.", + ], + }, + { + heading: "Testability and trust", + paragraphs: [ + "A RAG pipeline's quality shifts with its index, embeddings, and chunking; evaluating it is a bespoke project, and it carries its own injection risk — retrieved documents can contain hostile instructions. Skills on Super Agent Skill are adversarially tested against exactly that class of attack by attacker and judge LLMs, with the pass rate published, and re-tested over time via SkillForge.", + "Each skill is Ed25519-signed and scored by the public Trust Score formula (adversarial 45%, real success 20%, signing 10%, age 15%, schema 10%), verifiable offline with npm run trust:verify. Your retrieval corpus has no equivalent tamper-evidence unless you build it.", + ], + }, + { + heading: "Using them together", + paragraphs: [ + "The strongest pattern: a signed, tested skill defines the procedure and safety rails, and RAG supplies the facts the procedure operates on. The skill tells the agent how to treat retrieved content — including how not to obey instructions found inside it. Install is one MCP URL, so adding the procedural layer to an existing RAG stack is an afternoon, not a migration.", + ], + }, + ], + faqs: [ + { + question: "Does a skill replace my vector database?", + answer: + "No. Skills encode procedure and constraints; RAG serves fresh, large-scale knowledge. Replace RAG only if your 'knowledge' was actually procedure all along — checklists, policies, workflows — which fits naturally in a skill.", + }, + { + question: "Can skills reduce prompt-injection risk from retrieved documents?", + answer: + "That is one of the main things adversarial testing measures: an attacker LLM plants hostile instructions and a judge LLM scores whether the skill's constraints held. The pass rate is on the listing.", + }, + { + question: "How do I add a skill to an existing RAG agent?", + answer: + "Connect via MCP with a single URL or install with the npx CLI. The skill layers on top of your existing retrieval pipeline.", + }, + ], + verdict: + "Use RAG for what the agent should know, and signed, adversarially tested skills for how it should act. Domain expertise is both.", + }, + { + slug: "super-agent-skill-vs-langchain-hub", + title: "Super Agent Skill vs LangChain Hub prompts", + metaDescription: + "LangChain Hub shares prompts for LangChain apps; Super Agent Skill ships signed, adversarially tested skills over MCP. Compare trust, testing, and portability.", + lede: + "LangChain Hub is a genuinely useful sharing layer for prompts inside the LangChain ecosystem. But sharing and trusting are different problems. If your agent framework question is 'where do I find a prompt,' the Hub answers it; if it's 'what can I safely run in production,' the answer needs signatures and adversarial evidence.", + sections: [ + { + heading: "Distribution vs verification", + paragraphs: [ + "The Hub distributes community prompts with versioning and easy pulls into LangChain code. What it does not provide is cryptographic provenance or hostile testing: a pulled prompt is trusted on reputation, and its behavior under attack is unknown until your users find out.", + "Super Agent Skill treats verification as the product. Skills are Ed25519-signed, adversarially tested by attacker and judge LLMs with published pass rates, re-tested over time through SkillForge, and ranked by a Trust Score whose formula is public: adversarial 45%, real-world success 20%, signing 10%, age 15%, schema 10%. All of it is recomputable offline with npm run trust:verify.", + ], + }, + { + heading: "Ecosystem lock-in vs MCP", + paragraphs: [ + "Hub prompts are pulled into LangChain applications; that's their home. Skills install over MCP — an open protocol supported across agent runtimes — with a single URL, or via the npx CLI. The same signed skill serves whichever framework your team runs this quarter, which matters in a stack that changes as fast as agents do.", + ], + }, + { + heading: "Where the Hub still fits", + paragraphs: [ + "For rapid prototyping inside a LangChain codebase, pulling a Hub prompt is frictionless and fine. The transition point is the same as with any unsigned prompt source: the moment the agent touches credentials, customer data, or write-capable tools, provenance and adversarial evidence stop being optional.", + ], + }, + ], + faqs: [ + { + question: "Can I use Super Agent Skill with a LangChain app?", + answer: + "Yes — LangChain supports MCP, so skills install with one URL alongside your existing chains and tools.", + }, + { + question: "What's the practical difference for security review?", + answer: + "A Hub prompt arrives as trusted text; your team must red-team it themselves. A marketplace skill arrives with a published adversarial pass rate, an Ed25519 signature, and a Trust Score you can recompute offline.", + }, + ], + verdict: + "Prototype from the Hub if you live in LangChain. Ship to production from a registry that signs and adversarially tests what it distributes.", + }, + { + slug: "super-agent-skill-vs-custom-gpts", + title: "Super Agent Skill vs custom GPTs", + metaDescription: + "Custom GPTs live inside ChatGPT; MCP skills are portable, Ed25519-signed, and adversarially tested. Compare distribution, trust, and production readiness.", + lede: + "Custom GPTs made packaging instructions mainstream: give a system prompt a name and a storefront listing. For consumer chat, that's plenty. For teams wiring agents into real systems, the packaging is the easy part — portability, provenance, and testing are where custom GPTs stop and skills begin.", + sections: [ + { + heading: "Walled garden vs open protocol", + paragraphs: [ + "A custom GPT runs in one place: ChatGPT. Its instructions cannot follow you to another model, another runtime, or your own agent infrastructure. MCP skills install into any MCP-capable agent with a single URL or an npx command — the skill is an asset you own and move, not a configuration trapped in one vendor's UI.", + ], + }, + { + heading: "Trust: store listing vs verifiable evidence", + paragraphs: [ + "GPT store rankings run on usage and ratings — the same popularity signals that miss robustness entirely, and GPT instructions are famously extractable and modifiable by their owner at any time with no notice to users. There is no signature binding behavior to an author, and no published hostile-testing results.", + "Skills on Super Agent Skill are Ed25519-signed and tamper-evident, adversarially tested by attacker and judge LLMs with pass rates on the listing, and re-tested by SkillForge as models change. " + TRUST_FORMULA, + ], + }, + { + heading: "Production posture", + paragraphs: [ + "Custom GPTs are a consumer distribution channel; they don't slot into CI, code review, or a security team's audit workflow. Skills are text artifacts your team can review in a PR, verify offline, and pin by signature — the properties infrastructure teams already expect from every other dependency they ship.", + ], + }, + ], + faqs: [ + { + question: "Can I convert a custom GPT into a skill?", + answer: + "The instructions port naturally: package them as a skill, sign it, and submit it for adversarial testing. You gain portability across MCP runtimes plus a published Trust Score.", + }, + { + question: "Do skills work with OpenAI models?", + answer: + "Skills are model-agnostic instructions delivered over MCP; any MCP-capable agent runtime can install them, regardless of the underlying model vendor.", + }, + { + question: "Why does tamper-evidence matter here?", + answer: + "A custom GPT's owner can silently change its instructions after you've adopted it. A signed skill cannot change without breaking Ed25519 verification, which you can check yourself with npm run trust:verify.", + }, + ], + verdict: + "Custom GPTs are great storefronts for chat experiences. Agent infrastructure deserves dependencies that are portable, signed, and adversarially tested.", + }, +]; + +export function findComparison(slug: string): Comparison | undefined { + return COMPARISONS.find((c) => c.slug === slug); +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 23febadf..2ba5ce96 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -9,6 +9,7 @@ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. import { Route as rootRouteImport } from './routes/__root' +import { Route as WhitepaperRouteImport } from './routes/whitepaper' import { Route as WelcomeRouteImport } from './routes/welcome' import { Route as UseCasesRouteImport } from './routes/use-cases' import { Route as UploadRouteImport } from './routes/upload' @@ -32,6 +33,7 @@ import { Route as LeaderboardRouteImport } from './routes/leaderboard' import { Route as GenerateRouteImport } from './routes/generate' import { Route as ForgeRouteImport } from './routes/forge' import { Route as EvaluationRouteImport } from './routes/evaluation' +import { Route as EnterpriseRouteImport } from './routes/enterprise' import { Route as DocsRouteImport } from './routes/docs' import { Route as DiscoverRouteImport } from './routes/discover' import { Route as ContributorFaqRouteImport } from './routes/contributor-faq' @@ -43,6 +45,7 @@ import { Route as AdminRouteImport } from './routes/admin' import { Route as IndexRouteImport } from './routes/index' import { Route as PacksIndexRouteImport } from './routes/packs.index' import { Route as MarketplaceIndexRouteImport } from './routes/marketplace.index' +import { Route as CompareIndexRouteImport } from './routes/compare.index' import { Route as AdminIndexRouteImport } from './routes/admin.index' import { Route as UHandleRouteImport } from './routes/u.$handle' import { Route as SoulsSlugRouteImport } from './routes/souls.$slug' @@ -122,6 +125,11 @@ import { Route as ApiBadgesTrustSlugSvgRouteImport } from './routes/api/badges.t import { Route as ApiBadgesCertifiedIdSvgRouteImport } from './routes/api/badges.certified.$id.svg' import { Route as ApiPacksCustomizationIdDownloadExtRouteImport } from './routes/api/packs.customization.$id.download.$ext' +const WhitepaperRoute = WhitepaperRouteImport.update({ + id: '/whitepaper', + path: '/whitepaper', + getParentRoute: () => rootRouteImport, +} as any) const WelcomeRoute = WelcomeRouteImport.update({ id: '/welcome', path: '/welcome', @@ -237,6 +245,11 @@ const EvaluationRoute = EvaluationRouteImport.update({ path: '/evaluation', getParentRoute: () => rootRouteImport, } as any) +const EnterpriseRoute = EnterpriseRouteImport.update({ + id: '/enterprise', + path: '/enterprise', + getParentRoute: () => rootRouteImport, +} as any) const DocsRoute = DocsRouteImport.update({ id: '/docs', path: '/docs', @@ -292,6 +305,11 @@ const MarketplaceIndexRoute = MarketplaceIndexRouteImport.update({ path: '/marketplace/', getParentRoute: () => rootRouteImport, } as any) +const CompareIndexRoute = CompareIndexRouteImport.update({ + id: '/compare/', + path: '/compare/', + getParentRoute: () => rootRouteImport, +} as any) const AdminIndexRoute = AdminIndexRouteImport.update({ id: '/', path: '/', @@ -705,6 +723,7 @@ export interface FileRoutesByFullPath { '/contributor-faq': typeof ContributorFaqRoute '/discover': typeof DiscoverRoute '/docs': typeof DocsRouteWithChildren + '/enterprise': typeof EnterpriseRoute '/evaluation': typeof EvaluationRoute '/forge': typeof ForgeRouteWithChildren '/generate': typeof GenerateRoute @@ -728,6 +747,7 @@ export interface FileRoutesByFullPath { '/upload': typeof UploadRoute '/use-cases': typeof UseCasesRoute '/welcome': typeof WelcomeRoute + '/whitepaper': typeof WhitepaperRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/oauth-protected-resource': typeof DotwellKnownOauthProtectedResourceRouteWithChildren '/account/billing': typeof AccountBillingRoute @@ -772,6 +792,7 @@ export interface FileRoutesByFullPath { '/souls/$slug': typeof SoulsSlugRoute '/u/$handle': typeof UHandleRoute '/admin/': typeof AdminIndexRoute + '/compare/': typeof CompareIndexRoute '/marketplace/': typeof MarketplaceIndexRoute '/packs/': typeof PacksIndexRoute '/admin/import/github': typeof AdminImportGithubRoute @@ -818,6 +839,7 @@ export interface FileRoutesByTo { '/contributor-faq': typeof ContributorFaqRoute '/discover': typeof DiscoverRoute '/docs': typeof DocsRouteWithChildren + '/enterprise': typeof EnterpriseRoute '/evaluation': typeof EvaluationRoute '/forge': typeof ForgeRouteWithChildren '/generate': typeof GenerateRoute @@ -841,6 +863,7 @@ export interface FileRoutesByTo { '/upload': typeof UploadRoute '/use-cases': typeof UseCasesRoute '/welcome': typeof WelcomeRoute + '/whitepaper': typeof WhitepaperRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/oauth-protected-resource': typeof DotwellKnownOauthProtectedResourceRouteWithChildren '/account/billing': typeof AccountBillingRoute @@ -885,6 +908,7 @@ export interface FileRoutesByTo { '/souls/$slug': typeof SoulsSlugRoute '/u/$handle': typeof UHandleRoute '/admin': typeof AdminIndexRoute + '/compare': typeof CompareIndexRoute '/marketplace': typeof MarketplaceIndexRoute '/packs': typeof PacksIndexRoute '/admin/import/github': typeof AdminImportGithubRoute @@ -933,6 +957,7 @@ export interface FileRoutesById { '/contributor-faq': typeof ContributorFaqRoute '/discover': typeof DiscoverRoute '/docs': typeof DocsRouteWithChildren + '/enterprise': typeof EnterpriseRoute '/evaluation': typeof EvaluationRoute '/forge': typeof ForgeRouteWithChildren '/generate': typeof GenerateRoute @@ -956,6 +981,7 @@ export interface FileRoutesById { '/upload': typeof UploadRoute '/use-cases': typeof UseCasesRoute '/welcome': typeof WelcomeRoute + '/whitepaper': typeof WhitepaperRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/oauth-protected-resource': typeof DotwellKnownOauthProtectedResourceRouteWithChildren '/account/billing': typeof AccountBillingRoute @@ -1000,6 +1026,7 @@ export interface FileRoutesById { '/souls/$slug': typeof SoulsSlugRoute '/u/$handle': typeof UHandleRoute '/admin/': typeof AdminIndexRoute + '/compare/': typeof CompareIndexRoute '/marketplace/': typeof MarketplaceIndexRoute '/packs/': typeof PacksIndexRoute '/admin/import/github': typeof AdminImportGithubRoute @@ -1049,6 +1076,7 @@ export interface FileRouteTypes { | '/contributor-faq' | '/discover' | '/docs' + | '/enterprise' | '/evaluation' | '/forge' | '/generate' @@ -1072,6 +1100,7 @@ export interface FileRouteTypes { | '/upload' | '/use-cases' | '/welcome' + | '/whitepaper' | '/.well-known/oauth-authorization-server' | '/.well-known/oauth-protected-resource' | '/account/billing' @@ -1116,6 +1145,7 @@ export interface FileRouteTypes { | '/souls/$slug' | '/u/$handle' | '/admin/' + | '/compare/' | '/marketplace/' | '/packs/' | '/admin/import/github' @@ -1162,6 +1192,7 @@ export interface FileRouteTypes { | '/contributor-faq' | '/discover' | '/docs' + | '/enterprise' | '/evaluation' | '/forge' | '/generate' @@ -1185,6 +1216,7 @@ export interface FileRouteTypes { | '/upload' | '/use-cases' | '/welcome' + | '/whitepaper' | '/.well-known/oauth-authorization-server' | '/.well-known/oauth-protected-resource' | '/account/billing' @@ -1229,6 +1261,7 @@ export interface FileRouteTypes { | '/souls/$slug' | '/u/$handle' | '/admin' + | '/compare' | '/marketplace' | '/packs' | '/admin/import/github' @@ -1276,6 +1309,7 @@ export interface FileRouteTypes { | '/contributor-faq' | '/discover' | '/docs' + | '/enterprise' | '/evaluation' | '/forge' | '/generate' @@ -1299,6 +1333,7 @@ export interface FileRouteTypes { | '/upload' | '/use-cases' | '/welcome' + | '/whitepaper' | '/.well-known/oauth-authorization-server' | '/.well-known/oauth-protected-resource' | '/account/billing' @@ -1343,6 +1378,7 @@ export interface FileRouteTypes { | '/souls/$slug' | '/u/$handle' | '/admin/' + | '/compare/' | '/marketplace/' | '/packs/' | '/admin/import/github' @@ -1391,6 +1427,7 @@ export interface RootRouteChildren { ContributorFaqRoute: typeof ContributorFaqRoute DiscoverRoute: typeof DiscoverRoute DocsRoute: typeof DocsRouteWithChildren + EnterpriseRoute: typeof EnterpriseRoute EvaluationRoute: typeof EvaluationRoute ForgeRoute: typeof ForgeRouteWithChildren GenerateRoute: typeof GenerateRoute @@ -1414,6 +1451,7 @@ export interface RootRouteChildren { UploadRoute: typeof UploadRoute UseCasesRoute: typeof UseCasesRoute WelcomeRoute: typeof WelcomeRoute + WhitepaperRoute: typeof WhitepaperRoute DotwellKnownOauthAuthorizationServerRoute: typeof DotwellKnownOauthAuthorizationServerRouteWithChildren DotwellKnownOauthProtectedResourceRoute: typeof DotwellKnownOauthProtectedResourceRouteWithChildren AccountBillingRoute: typeof AccountBillingRoute @@ -1442,6 +1480,7 @@ export interface RootRouteChildren { RunSlugRoute: typeof RunSlugRoute SoulsSlugRoute: typeof SoulsSlugRoute UHandleRoute: typeof UHandleRoute + CompareIndexRoute: typeof CompareIndexRoute MarketplaceIndexRoute: typeof MarketplaceIndexRoute PacksIndexRoute: typeof PacksIndexRoute ApiAdminAiGatewayProbeRoute: typeof ApiAdminAiGatewayProbeRoute @@ -1473,6 +1512,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/whitepaper': { + id: '/whitepaper' + path: '/whitepaper' + fullPath: '/whitepaper' + preLoaderRoute: typeof WhitepaperRouteImport + parentRoute: typeof rootRouteImport + } '/welcome': { id: '/welcome' path: '/welcome' @@ -1634,6 +1680,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof EvaluationRouteImport parentRoute: typeof rootRouteImport } + '/enterprise': { + id: '/enterprise' + path: '/enterprise' + fullPath: '/enterprise' + preLoaderRoute: typeof EnterpriseRouteImport + parentRoute: typeof rootRouteImport + } '/docs': { id: '/docs' path: '/docs' @@ -1711,6 +1764,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MarketplaceIndexRouteImport parentRoute: typeof rootRouteImport } + '/compare/': { + id: '/compare/' + path: '/compare' + fullPath: '/compare/' + preLoaderRoute: typeof CompareIndexRouteImport + parentRoute: typeof rootRouteImport + } '/admin/': { id: '/admin/' path: '/' @@ -2427,6 +2487,7 @@ const rootRouteChildren: RootRouteChildren = { ContributorFaqRoute: ContributorFaqRoute, DiscoverRoute: DiscoverRoute, DocsRoute: DocsRouteWithChildren, + EnterpriseRoute: EnterpriseRoute, EvaluationRoute: EvaluationRoute, ForgeRoute: ForgeRouteWithChildren, GenerateRoute: GenerateRoute, @@ -2450,6 +2511,7 @@ const rootRouteChildren: RootRouteChildren = { UploadRoute: UploadRoute, UseCasesRoute: UseCasesRoute, WelcomeRoute: WelcomeRoute, + WhitepaperRoute: WhitepaperRoute, DotwellKnownOauthAuthorizationServerRoute: DotwellKnownOauthAuthorizationServerRouteWithChildren, DotwellKnownOauthProtectedResourceRoute: @@ -2480,6 +2542,7 @@ const rootRouteChildren: RootRouteChildren = { RunSlugRoute: RunSlugRoute, SoulsSlugRoute: SoulsSlugRoute, UHandleRoute: UHandleRoute, + CompareIndexRoute: CompareIndexRoute, MarketplaceIndexRoute: MarketplaceIndexRoute, PacksIndexRoute: PacksIndexRoute, ApiAdminAiGatewayProbeRoute: ApiAdminAiGatewayProbeRoute, diff --git a/src/routes/compare.$pair.tsx b/src/routes/compare.$pair.tsx index 95d5e917..142b7f8c 100644 --- a/src/routes/compare.$pair.tsx +++ b/src/routes/compare.$pair.tsx @@ -1,6 +1,8 @@ import { createFileRoute } from "@tanstack/react-router"; import { SitePage } from "@/components/site/SitePage"; import { supabaseAdmin as _supabaseAdmin } from "@/integrations/supabase/client.server"; +import { findComparison, type Comparison } from "@/lib/seo/comparisons"; +import { canonicalLink, SITE_URL } from "@/lib/seo/canonical"; const supabaseAdmin = _supabaseAdmin as any; // SEO landing: /compare/<slug-a>-vs-<slug-b> @@ -34,25 +36,80 @@ async function loadSide(slug: string): Promise<Side> { }; } +type LoaderData = + | { kind: "editorial"; comparison: Comparison } + | { kind: "pair"; left: Side; right: Side }; + export const Route = createFileRoute("/compare/$pair")({ - loader: async ({ params }) => { + loader: async ({ params }): Promise<LoaderData> => { + // Curated editorial comparisons take precedence over package pairs. + const comparison = findComparison(params.pair); + if (comparison) return { kind: "editorial", comparison }; const m = params.pair.match(/^([a-z0-9-]+)-vs-([a-z0-9-]+)$/); if (!m) throw new Response("invalid pair", { status: 400 }); const [, a, b] = m; const [left, right] = await Promise.all([loadSide(a), loadSide(b)]); - return { left, right }; + return { kind: "pair", left, right }; + }, + head: ({ loaderData, params }) => { + if (loaderData?.kind === "editorial") { + const c = loaderData.comparison; + const url = `${SITE_URL}/compare/${c.slug}`; + return { + meta: [ + { title: `${c.title} — Super Agent Skill` }, + { name: "description", content: c.metaDescription }, + { property: "og:title", content: c.title }, + { property: "og:description", content: c.metaDescription }, + { property: "og:type", content: "article" }, + { property: "og:url", content: url }, + ], + links: [canonicalLink(`/compare/${c.slug}`)], + scripts: [ + { + type: "application/ld+json", + children: JSON.stringify({ + "@context": "https://schema.org", + "@type": "FAQPage", + mainEntity: c.faqs.map((f) => ({ + "@type": "Question", + name: f.question, + acceptedAnswer: { "@type": "Answer", text: f.answer }, + })), + }), + }, + { + type: "application/ld+json", + children: JSON.stringify({ + "@context": "https://schema.org", + "@type": "Article", + headline: c.title, + description: c.metaDescription, + url, + author: { "@type": "Organization", name: "Super Agent Skill" }, + publisher: { "@type": "Organization", name: "Super Agent Skill", url: SITE_URL }, + }), + }, + ], + }; + } + const left = loaderData?.kind === "pair" ? loaderData.left : null; + const right = loaderData?.kind === "pair" ? loaderData.right : null; + return { + meta: [ + { title: `${left?.slug ?? ""} vs ${right?.slug ?? ""} — Super Agent Skill` }, + { name: "description", content: `Head-to-head comparison of ${left?.name ?? left?.slug} and ${right?.name ?? right?.slug} on trust score, adversarial robustness, and recent usage.` }, + ], + links: params?.pair ? [canonicalLink(`/compare/${params.pair}`)] : [], + }; }, - head: ({ loaderData }) => ({ - meta: [ - { title: `${loaderData?.left.slug} vs ${loaderData?.right.slug} — Super Agent Skill` }, - { name: "description", content: `Head-to-head comparison of ${loaderData?.left.name ?? loaderData?.left.slug} and ${loaderData?.right.name ?? loaderData?.right.slug} on trust score, adversarial robustness, and recent usage.` }, - ], - }), component: ComparePage, }); function ComparePage() { - const { left, right } = Route.useLoaderData(); + const data = Route.useLoaderData(); + if (data.kind === "editorial") return <EditorialCompare comparison={data.comparison} />; + const { left, right } = data; return ( <SitePage> <main className="mx-auto max-w-4xl p-6"> @@ -152,3 +209,65 @@ function ScoreBar({ value }: { value: number | null }) { function pct(n: number | null): string { return n === null ? "—" : `${(Number(n) * 100).toFixed(0)}%`; } + +function EditorialCompare({ comparison: c }: { comparison: Comparison }) { + return ( + <SitePage> + <main className="mx-auto max-w-3xl p-6"> + <nav className="mb-4 text-sm text-muted-foreground"> + <a href="/compare" className="hover:underline">Comparisons</a> + <span className="mx-2">/</span> + <span>{c.title}</span> + </nav> + <h1 className="text-3xl font-bold mb-4">{c.title}</h1> + <p className="text-lg text-muted-foreground mb-8">{c.lede}</p> + + {c.sections.map((s) => ( + <section key={s.heading} className="mb-8"> + <h2 className="text-xl font-semibold mb-3">{s.heading}</h2> + {s.paragraphs.map((p, i) => ( + <p key={i} className="mb-3 leading-relaxed text-foreground/90">{p}</p> + ))} + </section> + ))} + + <section className="mb-10 border rounded-lg p-5 bg-muted/40"> + <h2 className="text-xl font-semibold mb-2">The verdict</h2> + <p className="leading-relaxed">{c.verdict}</p> + </section> + + <section className="mb-10"> + <h2 className="text-xl font-semibold mb-4">Frequently asked questions</h2> + <div className="space-y-5"> + {c.faqs.map((f) => ( + <div key={f.question}> + <h3 className="font-medium mb-1">{f.question}</h3> + <p className="text-muted-foreground leading-relaxed">{f.answer}</p> + </div> + ))} + </div> + </section> + + <section className="grid gap-4 sm:grid-cols-2 mb-8"> + <a href="/marketplace" className="border rounded-lg p-5 hover:bg-accent"> + <div className="font-semibold mb-1">Browse verified skills →</div> + <p className="text-sm text-muted-foreground"> + Every listing shows its Trust Score, adversarial pass rate, and Ed25519 signing status. + </p> + </a> + <a href="/connect" className="border rounded-lg p-5 hover:bg-accent"> + <div className="font-semibold mb-1">Connect via MCP →</div> + <p className="text-sm text-muted-foreground"> + One URL installs the marketplace into any MCP-capable agent. No SDK, no migration. + </p> + </a> + </section> + + <p className="text-sm text-muted-foreground"> + Want to audit us? Run <code className="px-1 py-0.5 bg-muted rounded">npm run trust:verify</code> to + recompute any Trust Score and verify signatures offline. + </p> + </main> + </SitePage> + ); +} diff --git a/src/routes/compare.index.tsx b/src/routes/compare.index.tsx new file mode 100644 index 00000000..949521a8 --- /dev/null +++ b/src/routes/compare.index.tsx @@ -0,0 +1,70 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { SitePage } from "@/components/site/SitePage"; +import { COMPARISONS } from "@/lib/seo/comparisons"; +import { canonicalLink, SITE_URL } from "@/lib/seo/canonical"; + +export const Route = createFileRoute("/compare/")({ + head: () => ({ + meta: [ + { title: "Comparisons — Super Agent Skill vs the alternatives" }, + { + name: "description", + content: + "How Super Agent Skill compares to prompt repos, in-house builds, untested marketplaces, fine-tuning, RAG, LangChain Hub, and custom GPTs — with signing and adversarial testing explained.", + }, + { property: "og:title", content: "Comparisons — Super Agent Skill vs the alternatives" }, + ], + links: [canonicalLink("/compare")], + scripts: [ + { + type: "application/ld+json", + children: JSON.stringify({ + "@context": "https://schema.org", + "@type": "CollectionPage", + name: "Super Agent Skill comparisons", + url: `${SITE_URL}/compare`, + hasPart: COMPARISONS.map((c) => ({ + "@type": "Article", + headline: c.title, + url: `${SITE_URL}/compare/${c.slug}`, + })), + }), + }, + ], + }), + component: CompareIndexPage, +}); + +function CompareIndexPage() { + return ( + <SitePage> + <main className="mx-auto max-w-3xl p-6"> + <h1 className="text-3xl font-bold mb-2">Comparisons</h1> + <p className="text-muted-foreground mb-8"> + Honest, factual breakdowns of how signed, adversarially tested skills compare to the + alternatives — prompt repos, in-house builds, fine-tuning, RAG, and more. + </p> + <ul className="space-y-4"> + {COMPARISONS.map((c) => ( + <li key={c.slug}> + <a href={`/compare/${c.slug}`} className="block border rounded-lg p-5 hover:bg-accent"> + <div className="font-semibold mb-1">{c.title}</div> + <p className="text-sm text-muted-foreground">{c.metaDescription}</p> + </a> + </li> + ))} + </ul> + <div className="mt-10 grid gap-4 sm:grid-cols-2"> + <a href="/marketplace" className="border rounded-lg p-5 hover:bg-accent"> + <div className="font-semibold mb-1">Browse verified skills →</div> + <p className="text-sm text-muted-foreground">Trust Scores and adversarial pass rates on every listing.</p> + </a> + <a href="/connect" className="border rounded-lg p-5 hover:bg-accent"> + <div className="font-semibold mb-1">Connect via MCP →</div> + <p className="text-sm text-muted-foreground">One URL installs the marketplace into any MCP-capable agent.</p> + </a> + </div> + </main> + </SitePage> + ); +} diff --git a/src/routes/sitemap[.]xml.tsx b/src/routes/sitemap[.]xml.tsx index 19ca9435..d3b31ee5 100644 --- a/src/routes/sitemap[.]xml.tsx +++ b/src/routes/sitemap[.]xml.tsx @@ -1,6 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { supabaseAdmin as _supabaseAdmin } from "@/integrations/supabase/client.server"; import { USE_CASES } from "@/lib/seo/use-cases"; +import { COMPARISONS } from "@/lib/seo/comparisons"; const supabaseAdmin = _supabaseAdmin as any; @@ -103,6 +104,15 @@ export const Route = createFileRoute("/sitemap.xml")({ changefreq: r.changefreq, })); + const compareUrls: UrlEntry[] = [ + { loc: `${SITE}/compare`, priority: 0.7, changefreq: "weekly" }, + ...COMPARISONS.map((c) => ({ + loc: `${SITE}/compare/${c.slug}`, + priority: 0.7, + changefreq: "monthly", + })), + ]; + const useCaseUrls: UrlEntry[] = USE_CASES.map((u) => ({ loc: `${SITE}/use-case/${u.vertical}/${u.task}`, priority: 0.65, @@ -114,7 +124,7 @@ export const Route = createFileRoute("/sitemap.xml")({ fetchCollectionUrls().catch(() => [] as UrlEntry[]), ]); - const urls = [...staticUrls, ...useCaseUrls, ...packageUrls, ...collectionUrls]; + const urls = [...staticUrls, ...compareUrls, ...useCaseUrls, ...packageUrls, ...collectionUrls]; return new Response(buildXml(urls), { headers: { "Content-Type": "application/xml; charset=utf-8", From d9ec02d3d1dbca4b78496e77168e6faf8a5c2366 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Sun, 5 Jul 2026 01:50:48 +0000 Subject: [PATCH 3/3] Add enterprise page, security whitepaper, and sample trust attestation - /enterprise: security-buyer hero, verification mechanics with real Trust Score weights, compliance packs, Book a Trust Score walkthrough CTA - /whitepaper: AI Skill Supply-Chain Security document (threat model, mitigations, verification workflow, shared responsibility) - Downloadable sample trust attestation matching the real attestation shape (signature clearly marked SAMPLE) - Footer Enterprise link; both routes added to sitemap Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SqR7Sa8zNjf5MDDeg5f7vc --- public/sample-trust-attestation.json | 28 +++ src/components/site/Footer.tsx | 1 + src/routes/enterprise.tsx | 306 +++++++++++++++++++++++++++ src/routes/sitemap[.]xml.tsx | 2 + src/routes/whitepaper.tsx | 281 ++++++++++++++++++++++++ 5 files changed, 618 insertions(+) create mode 100644 public/sample-trust-attestation.json create mode 100644 src/routes/enterprise.tsx create mode 100644 src/routes/whitepaper.tsx diff --git a/public/sample-trust-attestation.json b/public/sample-trust-attestation.json new file mode 100644 index 00000000..e9f1a8e1 --- /dev/null +++ b/public/sample-trust-attestation.json @@ -0,0 +1,28 @@ +{ + "_sample": "This is a SAMPLE attestation for evaluation purposes. The signature and key id are placeholders and will NOT verify. Generate a real one with `npm run trust:attest` and verify with `npm run trust:verify`.", + "schema_version": 1, + "attested_at": "2026-07-01T12:00:00.000Z", + "package": { + "type": "skill", + "slug": "code-reviewer", + "version": "1.4.2", + "file": "code-reviewer.yaml" + }, + "content_sha256": "9f2b1c4a7e8d3f60b5a2c9d1e4f7a0b3c6d9e2f5a8b1c4d7e0f3a6b9c2d5e8f1", + "adversarial": { + "total": 42, + "passed": 41, + "failed": 1, + "pass_rate": 0.976, + "severity_weighted_score": 0.958, + "by_severity": { + "low": { "total": 10, "passed": 10 }, + "medium": { "total": 14, "passed": 14 }, + "high": { "total": 12, "passed": 11 }, + "critical": { "total": 6, "passed": 6 } + } + }, + "signing_key_id": "SAMPLE0000000000", + "algorithm": "ed25519", + "signature": "SAMPLE-SIGNATURE-NOT-VALID-base64-placeholder-do-not-use-in-production==" +} diff --git a/src/components/site/Footer.tsx b/src/components/site/Footer.tsx index 330d9e13..6d454502 100644 --- a/src/components/site/Footer.tsx +++ b/src/components/site/Footer.tsx @@ -29,6 +29,7 @@ export function Footer() { { label: "Marketplace", to: "/marketplace" }, { label: "Packs", to: "/packs" }, { label: "Pricing", to: "/pricing" }, + { label: "Enterprise", to: "/enterprise" }, { label: "Docs", to: "/docs" }, ]} /> <FooterCol title="Creators" links={[ diff --git a/src/routes/enterprise.tsx b/src/routes/enterprise.tsx new file mode 100644 index 00000000..5ac88fe1 --- /dev/null +++ b/src/routes/enterprise.tsx @@ -0,0 +1,306 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { Nav } from "@/components/site/Nav"; +import { Footer } from "@/components/site/Footer"; + +export const Route = createFileRoute("/enterprise")({ + component: EnterprisePage, + head: () => ({ + meta: [ + { title: "Enterprise · Super Agent Skill" }, + { + name: "description", + content: + "A signed skill supply chain your security team can audit: Ed25519-signed releases, adversarial testing, offline-verifiable trust attestations and a transparent Trust Score.", + }, + ], + }), +}); + +const WEIGHTS: Array<{ key: string; label: string; weight: number; note: string }> = [ + { key: "adv_weighted", label: "Adversarial severity-weighted score", weight: 0.25, note: "Failures on critical attack classes cost 8x more than low-severity ones." }, + { key: "adv_pass", label: "Adversarial pass rate", weight: 0.20, note: "Share of curated attack cases (injection, exfiltration, policy bypass) the package refuses correctly." }, + { key: "real_world", label: "Real-world success rate", weight: 0.20, note: "Anonymized execution telemetry from live agent runs over the last 30 days." }, + { key: "schema", label: "Schema validity", weight: 0.10, note: "Package validates against the public content schema." }, + { key: "signed", label: "Signed releases", weight: 0.10, note: "Ed25519-signed versions; full credit at 3+ signed releases." }, + { key: "age", label: "Package age", weight: 0.05, note: "Logarithmic — longevity helps but cannot buy trust." }, + { key: "ownership", label: "Owner 2FA", weight: 0.05, note: "Publisher account protected with two-factor authentication." }, + { key: "contributors", label: "Contributor count", weight: 0.05, note: "More independent eyes on the package, capped at 5." }, +]; + +function EnterprisePage() { + const mailto = + "mailto:enterprise@superagentskill.com?subject=Trust%20Score%20walkthrough%20request"; + + return ( + <div className="min-h-screen bg-background"> + <Nav /> + <main className="mx-auto max-w-7xl px-6 pb-24 pt-12"> + {/* Hero */} + <header className="max-w-3xl"> + <div className="mb-4 inline-flex items-center gap-2 rounded-full border border-border bg-surface px-3 py-1 text-xs"> + <span className="size-1.5 rounded-full bg-signal pulse-dot" /> + <span className="font-mono uppercase tracking-wider text-muted-foreground"> + For security & compliance teams + </span> + </div> + <h1 className="text-4xl font-semibold tracking-tight md:text-5xl"> + A skill supply chain your security team can actually audit. + </h1> + <p className="mt-4 text-lg text-muted-foreground"> + Every package is Ed25519-signed, adversarially tested before publish, and scored by a + public, transparent Trust Score formula. Signatures and trust attestations verify{" "} + <span className="font-medium text-foreground">offline</span> — no need to trust our + hosted endpoints, even in air-gapped environments. + </p> + <div className="mt-8 flex flex-wrap gap-3"> + <a + href={mailto} + className="inline-flex h-11 items-center rounded-md bg-primary px-5 text-sm font-medium text-primary-foreground shadow-sm transition-all hover:opacity-95" + > + Book a Trust Score walkthrough + </a> + <Link + to="/whitepaper" + className="inline-flex h-11 items-center rounded-md border border-border bg-surface px-5 text-sm font-medium transition-colors hover:border-primary/40" + > + Read the security whitepaper + </Link> + </div> + <div className="mt-8 grid grid-cols-2 gap-4 md:grid-cols-4"> + <HeroStat label="Release signing" value="Ed25519" /> + <HeroStat label="Verification" value="Offline" /> + <HeroStat label="Trust formula" value="Public" /> + <HeroStat label="Re-testing" value="Continuous" /> + </div> + </header> + + {/* How verification works */} + <section className="mt-20"> + <h2 className="text-2xl font-semibold tracking-tight">How verification works</h2> + <p className="mt-2 max-w-2xl text-muted-foreground"> + Four independent layers, each of which your team can inspect or reproduce. + </p> + <div className="mt-6 grid gap-4 md:grid-cols-2"> + <Card + step="01" + title="Ed25519-signed releases" + body="Every published version is cryptographically signed with the release key. The signature binds the exact content bytes — a single changed character fails verification. Verify offline with npm run release:verify; no network, no registry trust required." + /> + <Card + step="02" + title="Adversarial harness" + body="Before a package can publish, it is benchmarked against curated attack cases — prompt injection, jailbreaks, data exfiltration, policy bypass, PII/PHI leakage — with an LLM judge scoring whether the package actually refused, not just whether it used refusal keywords. Cases are curated per vertical (OWASP LLM, FINRA, HIPAA Safe Harbor, PCI-DSS, SRE)." + /> + <Card + step="03" + title="Transparent Trust Score" + body="The score is a public weighted formula over objective signals — the weights are below, the code is open source (src/lib/trust/score.ts). Unproven signals default to a conservative 0.3 prior, and evidence older than 180 days decays back toward that prior, so no package coasts on a stale benchmark. Each score ships with a confidence figure: how much is evidence-backed vs. assumed." + /> + <Card + step="04" + title="SkillForge continuous re-testing" + body="Published packages are re-scored against the live adversarial suite and real execution telemetry. When robustness drops, SkillForge flags drift and proposes patches — trust is earned per version, continuously, not granted once at publish time." + /> + </div> + + {/* Trust Score formula */} + <div className="mt-8 overflow-x-auto rounded-lg border border-border bg-surface"> + <table className="w-full min-w-[560px] text-left text-sm"> + <thead> + <tr className="border-b border-border text-xs uppercase tracking-wide text-muted-foreground"> + <th className="px-5 py-3">Trust Score component</th> + <th className="px-5 py-3">Weight</th> + <th className="px-5 py-3">What it measures</th> + </tr> + </thead> + <tbody> + {WEIGHTS.map((w) => ( + <tr key={w.key} className="border-b border-border/60 last:border-0"> + <td className="px-5 py-3 font-medium">{w.label}</td> + <td className="px-5 py-3 font-mono">{(w.weight * 100).toFixed(0)}%</td> + <td className="px-5 py-3 text-muted-foreground">{w.note}</td> + </tr> + ))} + </tbody> + </table> + </div> + <p className="mt-3 text-xs text-muted-foreground"> + Badge thresholds: green ≥ 0.85 · yellow ≥ 0.70 · orange ≥ 0.50 · red below. + The full implementation is open source in{" "} + <code className="rounded bg-muted px-1 py-0.5">src/lib/trust/score.ts</code>. + </p> + </section> + + {/* Verify before you buy */} + <section className="mt-20"> + <h2 className="text-2xl font-semibold tracking-tight">Verify before you buy</h2> + <p className="mt-2 max-w-2xl text-muted-foreground"> + Every package can ship with a signed trust attestation: a JSON document that binds the + package's exact SHA-256 content hash and version to its adversarial result, signed + with the Ed25519 release key. Your security team verifies it offline with nothing but + the package file, the attestation and our public key. + </p> + <div className="mt-6 grid gap-6 lg:grid-cols-2"> + <div className="rounded-lg border border-border bg-surface p-6"> + <h3 className="font-semibold">Download a sample attestation</h3> + <p className="mt-2 text-sm text-muted-foreground"> + Same shape as production output from{" "} + <code className="rounded bg-muted px-1 py-0.5 text-xs">npm run trust:attest</code>. + Signature values are clearly marked placeholders — it will (correctly) fail + verification, which is itself a demo of the verifier. + </p> + <a + href="/sample-trust-attestation.json" + download + className="mt-4 inline-flex h-10 items-center rounded-md border border-primary/40 bg-primary/10 px-4 text-sm font-medium text-primary transition-colors hover:bg-primary/15" + > + Download sample-trust-attestation.json + </a> + </div> + <div className="rounded-lg border border-border bg-surface p-6"> + <h3 className="font-semibold">Verify offline</h3> + <pre className="mt-3 overflow-x-auto rounded-md bg-muted p-4 font-mono text-xs leading-relaxed"> +{`# nothing but Node — no network, no registry trust +npm run trust:verify -- \\ + --attestation code-reviewer.trust.json \\ + --pkg code-reviewer.yaml \\ + --pubkey pub.pem + +# exits non-zero if the file was tampered with +# or the signature is invalid`} + </pre> + <p className="mt-3 text-sm text-muted-foreground"> + The verifier checks the public key fingerprint, recomputes the content hash and + validates the Ed25519 signature over a canonicalized payload. + </p> + </div> + </div> + </section> + + {/* Compliance */} + <section className="mt-20"> + <h2 className="text-2xl font-semibold tracking-tight">Built for regulated verticals</h2> + <p className="mt-2 max-w-2xl text-muted-foreground"> + Souls and guardrails encode the behavioral constraints your auditors ask about, and the + adversarial suite includes vertical-specific attack cases for fintech, healthcare, + security, and DevOps. + </p> + <div className="mt-6 grid gap-4 md:grid-cols-3"> + <ComplianceCard + title="Financial services" + items={[ + "Fintech Compliance Officer soul (SEC, FINRA, PCI-DSS, CFPB)", + "Adversarial cases: unlicensed investment advice, card-number leakage", + "No PII in Output guardrail (emails, SSNs, card numbers)", + ]} + /> + <ComplianceCard + title="Healthcare" + items={[ + "HIPAA-Aware Clinical Liaison soul (Safe Harbor de-identification)", + "Adversarial cases: PHI leakage, diagnosis-request refusal", + "No Medical Advice guardrail (no diagnoses, drugs or dosages)", + ]} + /> + <ComplianceCard + title="Audit & security" + items={[ + "SOC 2 Type II Auditor soul (Trust Services Criteria)", + "OWASP-LLM-aligned attack classes in the harness", + "Public SECURITY.md disclosure policy", + ]} + /> + </div> + <div className="mt-6 grid gap-4 md:grid-cols-2"> + <div className="rounded-lg border border-border bg-surface p-6"> + <h3 className="font-semibold">Telemetry & data flow</h3> + <p className="mt-2 text-sm text-muted-foreground"> + Skill lookups go from your agent to the MCP gateway over HTTPS; the gateway returns + signed package content from the public registry. The CLI sends anonymized install + telemetry only — disable it entirely with{" "} + <code className="rounded bg-muted px-1 py-0.5 text-xs">SUPER_AGENT_TELEMETRY=0</code>. + Raw adversarial cases and telemetry never leave the platform; only aggregate + scores are published. + </p> + </div> + <div className="rounded-lg border border-border bg-surface p-6"> + <h3 className="font-semibold">Air-gapped friendly</h3> + <p className="mt-2 text-sm text-muted-foreground"> + Release-bundle signatures and trust attestations verify with dependency-free Node + scripts. Hand your security team the package file, the attestation and the public + key — they can confirm exactly what was tested, with no network access at all. + </p> + </div> + </div> + </section> + + {/* CTA */} + <section className="mt-20 rounded-lg border border-primary/30 bg-primary/5 p-8 md:p-10"> + <h2 className="text-2xl font-semibold tracking-tight"> + See the Trust Score on your own use case + </h2> + <p className="mt-2 max-w-2xl text-muted-foreground"> + We'll walk your security team through the signing pipeline, the adversarial + harness and a live attestation verification — on a package relevant to your vertical. + </p> + <div className="mt-6 flex flex-wrap gap-3"> + <a + href={mailto} + className="inline-flex h-11 items-center rounded-md bg-primary px-5 text-sm font-medium text-primary-foreground shadow-sm transition-all hover:opacity-95" + > + Book a Trust Score walkthrough + </a> + <Link + to="/whitepaper" + className="inline-flex h-11 items-center rounded-md border border-border bg-surface px-5 text-sm font-medium transition-colors hover:border-primary/40" + > + Read the security whitepaper + </Link> + <Link + to="/evaluation" + className="inline-flex h-11 items-center rounded-md border border-border bg-surface px-5 text-sm font-medium transition-colors hover:border-primary/40" + > + Browse live evaluation data + </Link> + </div> + </section> + </main> + <Footer /> + </div> + ); +} + +function HeroStat({ label, value }: { label: string; value: string }) { + return ( + <div className="rounded-lg border border-border bg-surface p-4"> + <div className="text-xs uppercase tracking-wide text-muted-foreground">{label}</div> + <div className="mt-1 font-mono text-lg font-semibold">{value}</div> + </div> + ); +} + +function Card({ step, title, body }: { step: string; title: string; body: string }) { + return ( + <div className="rounded-lg border border-border bg-surface p-6"> + <div className="font-mono text-xs text-primary">{step}</div> + <h3 className="mt-2 font-semibold">{title}</h3> + <p className="mt-2 text-sm text-muted-foreground">{body}</p> + </div> + ); +} + +function ComplianceCard({ title, items }: { title: string; items: string[] }) { + return ( + <div className="rounded-lg border border-border bg-surface p-6"> + <h3 className="font-semibold">{title}</h3> + <ul className="mt-3 space-y-2 text-sm text-muted-foreground"> + {items.map((it) => ( + <li key={it} className="flex gap-2"> + <span className="mt-1 size-1.5 shrink-0 rounded-full bg-primary" /> + <span>{it}</span> + </li> + ))} + </ul> + </div> + ); +} diff --git a/src/routes/sitemap[.]xml.tsx b/src/routes/sitemap[.]xml.tsx index d3b31ee5..bbaa3dd4 100644 --- a/src/routes/sitemap[.]xml.tsx +++ b/src/routes/sitemap[.]xml.tsx @@ -15,6 +15,8 @@ const STATIC_ROUTES: Array<{ path: string; priority: number; changefreq: string { path: "/pricing", priority: 0.9, changefreq: "weekly" }, { path: "/docs", priority: 0.9, changefreq: "weekly" }, { path: "/docs/mcp", priority: 0.85, changefreq: "weekly" }, + { path: "/enterprise", priority: 0.85, changefreq: "monthly" }, + { path: "/whitepaper", priority: 0.75, changefreq: "monthly" }, { path: "/marketplace", priority: 0.85, changefreq: "daily" }, { path: "/marketplace/categories", priority: 0.7, changefreq: "weekly" }, { path: "/marketplace/rankings", priority: 0.7, changefreq: "daily" }, diff --git a/src/routes/whitepaper.tsx b/src/routes/whitepaper.tsx new file mode 100644 index 00000000..d4a85c59 --- /dev/null +++ b/src/routes/whitepaper.tsx @@ -0,0 +1,281 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { Nav } from "@/components/site/Nav"; +import { Footer } from "@/components/site/Footer"; + +export const Route = createFileRoute("/whitepaper")({ + component: WhitepaperPage, + head: () => ({ + meta: [ + { title: "AI Skill Supply-Chain Security · Super Agent Skill" }, + { + name: "description", + content: + "Whitepaper: the threat model for AI agent skill supply chains, and how Ed25519 signing, adversarial testing and transparent trust scoring mitigate each threat.", + }, + ], + }), +}); + +function H2({ id, children }: { id: string; children: React.ReactNode }) { + return ( + <h2 id={id} className="mt-14 scroll-mt-24 text-2xl font-semibold tracking-tight"> + {children} + </h2> + ); +} + +function H3({ children }: { children: React.ReactNode }) { + return <h3 className="mt-8 text-lg font-semibold tracking-tight">{children}</h3>; +} + +function P({ children }: { children: React.ReactNode }) { + return <p className="mt-3 leading-relaxed text-muted-foreground">{children}</p>; +} + +function Code({ children }: { children: React.ReactNode }) { + return <code className="rounded bg-muted px-1 py-0.5 font-mono text-xs text-foreground">{children}</code>; +} + +const TOC = [ + ["abstract", "Abstract"], + ["threat-model", "1. Threat model"], + ["mitigations", "2. Mitigations"], + ["workflow", "3. Verification workflow"], + ["shared-responsibility", "4. Shared-responsibility model"], + ["conclusion", "5. Conclusion"], +] as const; + +function WhitepaperPage() { + return ( + <div className="min-h-screen bg-background"> + <Nav /> + <main className="mx-auto max-w-3xl px-6 pb-24 pt-12"> + <div className="mb-4 inline-flex items-center gap-2 rounded-full border border-border bg-surface px-3 py-1 text-xs"> + <span className="font-mono uppercase tracking-wider text-muted-foreground"> + Security whitepaper + </span> + </div> + <h1 className="text-3xl font-semibold tracking-tight md:text-4xl"> + AI Skill Supply-Chain Security + </h1> + <p className="mt-3 text-muted-foreground"> + How Super Agent Skill secures the path from a published skill to your running agent: + cryptographic signing, adversarial testing, transparent trust scoring, and continuous + re-evaluation. + </p> + + <nav className="mt-8 rounded-lg border border-border bg-surface p-5 text-sm"> + <div className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Contents</div> + <ul className="mt-3 space-y-1.5"> + {TOC.map(([id, label]) => ( + <li key={id}> + <a href={`#${id}`} className="text-muted-foreground transition-colors hover:text-foreground"> + {label} + </a> + </li> + ))} + </ul> + </nav> + + <H2 id="abstract">Abstract</H2> + <P> + AI agents increasingly load third-party instructions — skills, playbooks, personas + (souls) and guardrails — at runtime. These packages are natural-language programs: they + direct model behavior with the same authority as first-party prompts. That makes the + skill registry a supply chain, and an unsigned, untested skill registry is the npm of + 2010: convenient, and one compromised package away from an incident. This document + describes the threat model we design against and the controls the platform applies at + publish time, at install time, and continuously after release. + </P> + + <H2 id="threat-model">1. Threat model</H2> + + <H3>1.1 Prompt injection via package content</H3> + <P> + A skill's markdown body is fed to the model. A malicious or compromised package can + embed instructions that override the agent's system prompt, exfiltrate conversation + context, or redirect tool calls. Unlike code, the payload is prose — static scanners + built for binaries do not see it. + </P> + + <H3>1.2 Malicious or backdoored skills</H3> + <P> + An attacker publishes a package that behaves correctly in demos but leaks PII/PHI, + gives unlicensed financial or medical advice, or bypasses stated policies under + adversarial phrasing. The failure mode is behavioral, so it only appears under attack — + which is why we test with attacks, not just happy-path examples. + </P> + + <H3>1.3 Supply-chain tampering</H3> + <P> + Classic package-registry threats apply directly: account takeover of a publisher, + modification of package content between review and delivery, a compromised registry or + CDN serving different bytes than were tested, and version-substitution attacks where a + benign version is reviewed but a different one is installed. + </P> + + <H3>1.4 Trust decay</H3> + <P> + A package that passed testing a year ago may fail today: attack techniques evolve, the + underlying models change, and maintainers drift. A one-time "verified" badge is a + liability if nothing re-earns it. + </P> + + <H2 id="mitigations">2. Mitigations</H2> + + <H3>2.1 Ed25519-signed releases (vs. tampering)</H3> + <P> + Every published version is signed with an Ed25519 release key. The signature covers the + exact content bytes; verification (<Code>npm run release:verify</Code>) is a + dependency-free Node script that runs offline. This defeats in-transit and at-rest + tampering: what your security team reviewed is bit-for-bit what your agent loads, and a + compromised registry cannot forge a valid signature. + </P> + + <H3>2.2 Adversarial harness (vs. injection and malicious behavior)</H3> + <P> + Before publish, every package is benchmarked against a curated adversarial suite: + prompt injection, jailbreaks, data exfiltration, blast-radius, policy bypass, and + PII/PHI leakage cases, organized per vertical (OWASP LLM, FINRA, HIPAA Safe Harbor, + PCI-DSS, SRE). Outcomes are judged by an LLM judge rather than keyword matching, because + keyword heuristics miss polite-but-compliant answers and over-fire on outputs that + merely quote refusal language. Each case carries a severity (low 1x, medium 2x, high + 4x, critical 8x), producing both a pass rate and a severity-weighted score. + </P> + + <H3>2.3 Transparent Trust Score (vs. blind trust)</H3> + <P> + The Trust Score is a public weighted formula over objective signals — adversarial + severity-weighted score (25%), adversarial pass rate (20%), real-world success rate + (20%), schema validity (10%), signed releases (10%), package age (5%), owner 2FA (5%), + contributor count (5%). Two properties matter for auditors: unproven signals default to + a conservative 0.3 prior rather than a neutral 0.5, so untested packages cannot float + into "acceptable" for free; and measured evidence decays linearly toward that prior over + 180 days, so no package coasts on a stale benchmark. Every score carries a confidence + value stating how much of it is evidence-backed versus assumed. The implementation is + open source (<Code>src/lib/trust/score.ts</Code>). + </P> + + <H3>2.4 Signed trust attestations (vs. trusting our endpoints)</H3> + <P> + <Code>npm run trust:attest</Code> produces a JSON attestation binding a package's + SHA-256 content hash and version to its adversarial result, canonicalized + (recursively key-sorted) and signed with the release key. A consumer verifies it with{" "} + <Code>npm run trust:verify</Code> using only the package file, the attestation and the + publisher's public key — no network, no trust in the hosted Trust Score endpoint. The + verifier checks the key fingerprint, recomputes the content hash, and validates the + Ed25519 signature; any mismatch exits non-zero. + </P> + + <H3>2.5 SkillForge continuous re-testing (vs. trust decay)</H3> + <P> + Published packages are periodically re-scored against the live adversarial suite and + against anonymized execution telemetry (success rate, latency, drift, critical + findings — visible on the public <Link to="/evaluation" className="text-primary hover:underline">Evaluation</Link>{" "} + page). When robustness drops, the package is flagged and SkillForge proposes patches. + Evidence-age decay in the Trust Score formula enforces the same principle numerically. + </P> + + <H3>2.6 Guardrails and souls (defense in depth)</H3> + <P> + Behavioral constraints ship as first-class, individually tested packages: guardrails + such as <Code>no-pii-in-output</Code> and <Code>no-medical-advice</Code> enforce output + policy, while compliance souls (Fintech Compliance Officer, HIPAA-Aware Clinical + Liaison, SOC 2 Type II Auditor) constrain persona-level behavior for regulated + verticals. A skill failure is contained by the layers around it. + </P> + + <H2 id="workflow">3. Verification workflow</H2> + <P>A recommended evaluation flow for a security team assessing a package:</P> + <ol className="mt-3 list-decimal space-y-2 pl-6 text-muted-foreground"> + <li> + Review the public trust page at{" "} + <Code>superagentskill.com/marketplace/trust/<slug></Code>: adversarial pass rate, + signature status, Trust Score breakdown and confidence. + </li> + <li> + Obtain the package file and its signed attestation (<Code><slug>.trust.json</Code>). + </li> + <li> + Verify offline:{" "} + <Code>npm run trust:verify -- --attestation pkg.trust.json --pkg pkg.yaml --pubkey pub.pem</Code>. + A non-zero exit means tampering or an invalid signature. + </li> + <li> + Cross-check the attested <Code>content_sha256</Code> against your own hash of the file + you intend to deploy. + </li> + <li> + Optionally re-run the adversarial suite yourself: the harness ( + <Code>scripts/eval-adversarial.mjs</Code>) and case corpus ( + <Code>content/adversarial/</Code>) are open source. + </li> + <li> + Monitor post-deploy via the Evaluation page; treat critical findings or drift as a + re-review trigger. + </li> + </ol> + + <H2 id="shared-responsibility">4. Shared-responsibility model</H2> + <div className="mt-4 overflow-x-auto rounded-lg border border-border bg-surface"> + <table className="w-full min-w-[560px] text-left text-sm"> + <thead> + <tr className="border-b border-border text-xs uppercase tracking-wide text-muted-foreground"> + <th className="px-5 py-3">Layer</th> + <th className="px-5 py-3">Platform responsibility</th> + <th className="px-5 py-3">Customer responsibility</th> + </tr> + </thead> + <tbody className="text-muted-foreground"> + {[ + ["Package integrity", "Sign every release; publish attestations and the public key", "Verify signatures/attestations before deploying; pin versions"], + ["Behavioral safety", "Adversarial testing at publish; continuous re-scoring; public scores", "Choose Trust Score thresholds; layer guardrails and souls appropriate to your vertical"], + ["Data flow", "HTTPS-only gateway; signed content; anonymized, opt-out telemetry", "Set SUPER_AGENT_TELEMETRY=0 if required; review what context your agent shares with skills"], + ["Runtime", "Score real-world telemetry; flag drift and critical findings", "Least-privilege tool access for agents; monitor Evaluation data; re-review on drift"], + ["Disclosure", "Public SECURITY.md and coordinated disclosure process", "Report suspected vulnerabilities through the SECURITY.md channel"], + ].map(([layer, plat, cust]) => ( + <tr key={layer} className="border-b border-border/60 last:border-0 align-top"> + <td className="px-5 py-3 font-medium text-foreground">{layer}</td> + <td className="px-5 py-3">{plat}</td> + <td className="px-5 py-3">{cust}</td> + </tr> + ))} + </tbody> + </table> + </div> + + <H2 id="conclusion">5. Conclusion</H2> + <P> + No single control secures a natural-language supply chain. Signing proves what you got; + adversarial testing proves how it behaves under attack; transparent scoring makes the + evidence auditable; attestations make it verifiable without trusting us; continuous + re-testing keeps all of it current. Each layer is independently inspectable — and the + parts your auditors care about run offline, on your side of the boundary. + </P> + + <div className="mt-12 flex flex-wrap gap-3 border-t border-border pt-8"> + <Link + to="/enterprise" + className="inline-flex h-10 items-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow-sm transition-all hover:opacity-95" + > + Enterprise overview + </Link> + <a + href="/sample-trust-attestation.json" + download + className="inline-flex h-10 items-center rounded-md border border-border bg-surface px-4 text-sm font-medium transition-colors hover:border-primary/40" + > + Download sample attestation + </a> + <a + href="https://github.com/criptogus/agent-evolve-network/blob/main/SECURITY.md" + className="inline-flex h-10 items-center rounded-md border border-border bg-surface px-4 text-sm font-medium transition-colors hover:border-primary/40" + > + SECURITY.md + </a> + </div> + </main> + <Footer /> + </div> + ); +}