Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
40af7af
Add ATTESTATION-INTEGRATION.md: plan for surfacing attestations in MVR
mdgeorge4153 May 28, 2026
5154976
M1: mvr-api demo server + frontend localnet repoint
mdgeorge4153 May 29, 2026
00b84b8
demo: repoint all networks (not just mainnet) at the local stack
mdgeorge4153 May 29, 2026
738f49b
M2: Attestations tab reading from the registry over JSON-RPC
mdgeorge4153 May 29, 2026
43da69a
Trust Signals tab: polarity sections, count pills, attester avatars
mdgeorge4153 May 29, 2026
7bddb9f
Pass 2: vuln propagation, severity sort/chips, Security tab polish
mdgeorge4153 May 29, 2026
271bb68
Pass 2: link attesters to their MVR pages
mdgeorge4153 May 29, 2026
df7d10e
doc: mark pass 2 complete
mdgeorge4153 May 29, 2026
fbfe10c
Show attester MVR name instead of raw address; defer web-of-trust
mdgeorge4153 May 29, 2026
27aeeb0
Show vuln attester MVR names; give auditors distinct namespaces
mdgeorge4153 May 29, 2026
4b3d2a4
demo_server: seed git_info for auditor packages (sample READMEs)
mdgeorge4153 May 29, 2026
0bf9590
Make all displayed MVR names clickable links
mdgeorge4153 May 29, 2026
bb62324
Show MVR name in attestation types; consolidate the vuln row
mdgeorge4153 May 29, 2026
4a0d3c4
Trust tab: drop Active label, severity-colored vuln border, object ex…
mdgeorge4153 May 29, 2026
90109e2
Trust tab: fix severity border colors; per-band breakdown coloring
mdgeorge4153 May 29, 2026
ab527d8
Trust tab: inline-style severity colors + borders; report link in header
mdgeorge4153 May 29, 2026
ec435fd
M3: spam-proof server-side MatchAny over trusted attester types
mdgeorge4153 May 29, 2026
7d61572
Attestations: Issued tab on attester pages + whitelist-gated tabs
mdgeorge4153 Jun 1, 2026
386a505
docs: drop machine-specific demo-ids path; note WITH_GRAPHQL
Jun 3, 2026
8586632
MVR frontend: trusted-attestors page, trust badge, attestor icons
Jun 3, 2026
62c79bf
Fix stale ts/lib provenance refs (registry repo moved them to ts/src)
Jun 3, 2026
2cf92ee
Frontend: salt-revocation read-model + Issued-tab revocation
Jun 16, 2026
ca99354
Frontend: surface revoked attestations distinctly
Jun 17, 2026
19613d4
Frontend positive MVP: carve out negatives + revocation polish
Jun 17, 2026
ee60fff
demo trust config: audit_example -> auditor_a ("Auditor A")
mdgeorge4153 Jun 23, 2026
cff8743
Frontend PR-A cleanups: remove expiration; lib/hooks/script tidy
mdgeorge4153 Jun 24, 2026
b5d6819
attestation config: source default + env override (not env-only)
mdgeorge4153 Jun 24, 2026
a917d50
demo: fix auditor mvr name (@demo/auditor-a, not invalid @demo/audito…
mdgeorge4153 Jun 25, 2026
539a222
Frontend: client-side trust filtering; icons split; testnet gating; n…
mdgeorge4153 Jun 25, 2026
7c5951b
Follow attestation_registry -> attestations module rename
mdgeorge4153 Jun 25, 2026
4d7c852
Remove demo attester icons from the app; source them from the registr…
mdgeorge4153 Jun 25, 2026
a2deb3c
Add an example trust config so reviewers can see its shape
mdgeorge4153 Jun 25, 2026
07430fd
demo_server: fetch demo READMEs from the branch, not the stale mvr-de…
mdgeorge4153 Jun 25, 2026
4a8c2e2
Version selector for attestations; seed the demo dependency as multi-…
mdgeorge4153 Jun 25, 2026
73b8c84
Warning copy: 'This package version has no published audits'
mdgeorge4153 Jun 25, 2026
5bd1853
UI review: focus the Security view; make the Issued list scale
mdgeorge4153 Jun 25, 2026
9faacf8
Surface reverse-read GraphQL errors instead of swallowing them
mdgeorge4153 Jun 25, 2026
b6fc9f0
Surface forward-read errors on the Security tab too
mdgeorge4153 Jun 26, 2026
46d2b2b
Issued tab: surface per-object failures, split revoked, show descript…
mdgeorge4153 Jun 26, 2026
d99e48d
Version selector navigates to the version's page; surface revoked-rea…
mdgeorge4153 Jun 26, 2026
c967d5d
Versions tab: "Latest" marks the newest version, not the viewed one
mdgeorge4153 Jun 26, 2026
d16cb74
Security tab: show every version's audit status, not a navigating sel…
mdgeorge4153 Jun 26, 2026
4ad5e16
Trust-signal pill: show the count even at 0
mdgeorge4153 Jun 26, 2026
902282c
Security page heading: "Audits" (tab stays "Security")
mdgeorge4153 Jun 26, 2026
30ba1c7
Audit card: surface only standard Display conventions, not custom fields
mdgeorge4153 Jun 26, 2026
ad72986
demo_server: seed names for untrusted attestors too
mdgeorge4153 Jun 26, 2026
33da41a
Issued tab: viewable by URL for any package, with a not-trusted warning
mdgeorge4153 Jun 26, 2026
4ef12be
demo_server: seed @demo/attestations for the registry package
mdgeorge4153 Jun 26, 2026
2b55792
demo: per-org mvr names for verisimilitude
mdgeorge4153 Jun 26, 2026
2ecbb12
Issued tab: warning says "mvr's trusted list", not "your"
mdgeorge4153 Jun 26, 2026
d5946ec
demo_server: render the @mysten/attestations README
mdgeorge4153 Jun 26, 2026
421934c
demo: add Auditor C to the trust-config presentation map
mdgeorge4153 Jul 2, 2026
5ec2826
Issued tab: page through all results, render them all (drop see-more)
mdgeorge4153 Jul 2, 2026
48e04a2
demo_server: seed all versions of upgraded trusted attesters
mdgeorge4153 Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 292 additions & 0 deletions ATTESTATION-INTEGRATION.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@
# Example:
# SERVERVAR="foo"
# NEXT_PUBLIC_CLIENTVAR="bar"

# Optional: point ALL networks at a local stack (a localnet + the attestation
# demo server) so the app doesn't depend on live Sui infra. Leave unset for
# production. See ATTESTATION-INTEGRATION.md.
# NEXT_PUBLIC_LOCAL_RPC_URL="http://127.0.0.1:9000"
# NEXT_PUBLIC_LOCAL_MVR_ENDPOINT="http://127.0.0.1:8000"
79 changes: 79 additions & 0 deletions app/src/app/attestors/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"use client";

import { PlainPageLayout } from "@/components/layouts/PlainPageLayout";
import { Text } from "@/components/ui/Text";
import { AttesterAvatar } from "@/components/single-package/SinglePackageTrustSignals";
import { attestationConfig, type TrustedAttestor } from "@/lib/attestations";
import { beautifySuiAddress } from "@/lib/utils";

export default function TrustedAttestorsPage() {
const cfg = attestationConfig();
const attestors = cfg?.trustedAttestors ?? [];

return (
<PlainPageLayout>
<div className="flex flex-col gap-lg py-lg">
<div className="flex flex-col gap-xs">
<Text kind="heading" size="heading-regular">
Trusted attestors
</Text>
<Text
as="p"
kind="paragraph"
size="paragraph-small"
className="max-w-2xl text-content-secondary"
>
Attestations are surfaced only from this curated set of attesters.
Trust is a consumer-side choice — these are the packages MVR
recognizes as authoritative sources of audits and vulnerability
disclosures.
</Text>
</div>

{attestors.length === 0 ? (
<Text as="p" kind="paragraph" size="paragraph-small">
No trusted attestors are configured.
</Text>
) : (
<div className="flex flex-col gap-sm">
{attestors.map((a) => (
<AttestorCard key={a.originalId} attestor={a} />
))}
</div>
)}
</div>
</PlainPageLayout>
);
}

function AttestorCard({ attestor }: { attestor: TrustedAttestor }) {
return (
<div className="flex items-center gap-md rounded-md bg-bg-secondary p-md">
<AttesterAvatar attestor={attestor} size="lg" />
<div className="flex flex-col gap-2xs">
<Text kind="label" size="label-regular">
{attestor.name}
</Text>
{attestor.mvrName ? (
<a
href={`/package/${attestor.mvrName}`}
className="text-content-accent underline w-fit"
>
<Text kind="paragraph" size="paragraph-xs">
{attestor.mvrName}
</Text>
</a>
) : (
<Text
as="p"
kind="paragraph"
size="paragraph-xs"
className="font-mono opacity-60"
>
{beautifySuiAddress(attestor.originalId)}
</Text>
)}
</div>
</div>
);
}
24 changes: 16 additions & 8 deletions app/src/components/providers/client-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,23 @@ export type Clients = {
};
};

// When set (the local demo stack — a localnet + the attestation demo server),
// ALL networks are pointed at it so the app does not depend on live Sui infra.
// Unset in production, where the per-network defaults apply.
// See ATTESTATION-INTEGRATION.md.
const LOCAL_RPC = process.env.NEXT_PUBLIC_LOCAL_RPC_URL;
const LOCAL_MVR = process.env.NEXT_PUBLIC_LOCAL_MVR_ENDPOINT;
const LOCAL_GRAPHQL = process.env.NEXT_PUBLIC_LOCAL_GRAPHQL;

const mainnet = new SuiClient({
url: "https://suins-rpc.mainnet.sui.io:443",
url: LOCAL_RPC ?? "https://suins-rpc.mainnet.sui.io:443",
network: "mainnet",
});

export const DefaultClients: Clients = {
mainnet,
testnet: new SuiClient({
url: "https://suins-rpc.testnet.sui.io",
url: LOCAL_RPC ?? "https://suins-rpc.testnet.sui.io",
network: "testnet",
}),
devnet: new SuiClient({ url: getFullnodeUrl("devnet"), network: "devnet" }),
Expand All @@ -57,19 +65,19 @@ export const DefaultClients: Clients = {
},
graphql: {
mainnet: new SuiGraphQLClient({
url: "https://graphql.mainnet.sui.io/graphql",
url: LOCAL_GRAPHQL ?? "https://graphql.mainnet.sui.io/graphql",
}),
testnet: new SuiGraphQLClient({
url: "https://graphql.testnet.sui.io/graphql",
url: LOCAL_GRAPHQL ?? "https://graphql.testnet.sui.io/graphql",
}),
},
mvrEndpoints: {
mainnet: "https://mainnet.mvr.mystenlabs.com",
testnet: "https://testnet.mvr.mystenlabs.com",
mainnet: LOCAL_MVR ?? "https://mainnet.mvr.mystenlabs.com",
testnet: LOCAL_MVR ?? "https://testnet.mvr.mystenlabs.com",
},
mvrExperimentalEndpoints: {
mainnet: "https://qa.mainnet.mvr.mystenlabs.com",
testnet: "https://qa.testnet.mvr.mystenlabs.com",
mainnet: LOCAL_MVR ?? "https://qa.mainnet.mvr.mystenlabs.com",
testnet: LOCAL_MVR ?? "https://qa.testnet.mvr.mystenlabs.com",
},
};

Expand Down
62 changes: 60 additions & 2 deletions app/src/components/single-package/SinglePackage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import { ReadMeRenderer } from "./ReadMeRenderer";
import { SinglePackageDependencies } from "./SinglePackageDependencies";
import { SinglePackageDependents } from "./SinglePackageDependents";
import { SinglePackageVersions } from "./SinglePackageVersions";
import {
SinglePackageTrustSignals,
TrustSignalCount,
} from "./SinglePackageTrustSignals";
import {
SinglePackageIssued,
IssuedCount,
} from "./SinglePackageIssued";
import { DependenciesIconSelected } from "@/icons/single-package/DependenciesIcon";
import { DependendsIconSelected } from "@/icons/single-package/DependendsIcon";
import { DependenciesIconUnselected } from "@/icons/single-package/DependenciesIcon";
Expand All @@ -23,6 +31,24 @@ import { ReadMeIconUnselected } from "@/icons/single-package/ReadMeIcon";
import { SinglePackageTab } from "@/utils/types";
import { AnalyticsIconUnselected } from "@/icons/single-package/AnalyticsIcon";
import { AnalyticsIconSelected } from "@/icons/single-package/AnalyticsIcon";
import { attestationConfig, isConfiguredAttestor } from "@/lib/attestations";

// Simple shield-check glyph for the Trust Signals tab; reused for both states.
const TrustSignalsIcon = () => (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 3l7 3v5c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3z" />
<path d="M9 12l2 2 4-4" />
</svg>
);

// Upload/outbox glyph for the Issued tab (attestations this package emits).
const IssuedIcon = () => (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2" />
<path d="M12 15V3" />
<path d="M7 8l5-5 5 5" />
</svg>
);

export const Tabs: SinglePackageTab[] = [
{
Expand Down Expand Up @@ -68,6 +94,28 @@ export const Tabs: SinglePackageTab[] = [
) => <NameTotalDependentsCount name={name!.name} network={network} />,
component: (name: ResolvedName) => <SinglePackageDependents name={name} />,
},
{
key: "trust-signals",
title: "Security",
selectedIcon: <TrustSignalsIcon />,
unselectedIcon: <TrustSignalsIcon />,
label: (address: string, network: "mainnet" | "testnet") => (
<TrustSignalCount address={address} network={network} />
),
component: (name: ResolvedName) => <SinglePackageTrustSignals name={name} />,
},
{
key: "issued",
title: "Attestations",
selectedIcon: <IssuedIcon />,
unselectedIcon: <IssuedIcon />,
label: (
_address: string,
network: "mainnet" | "testnet",
name?: ResolvedName,
) => (name ? <IssuedCount name={name} network={network} /> : null),
component: (name: ResolvedName) => <SinglePackageIssued name={name} />,
},
{
key: "analytics",
title: "Analytics",
Expand All @@ -89,7 +137,17 @@ export function SinglePackage({
const router = useRouter();
const searchParams = useSearchParams();

const [activeTab, setActiveTab] = useState(Tabs[0]!.key);
// The "Issued" tab is in the nav only for configured attesters (a free
// in-memory check), but it stays reachable by URL (?tab=issued) for any
// package — the panel shows a not-trusted warning. So nav visibility
// (navTabs) and URL-selectability (the full Tabs list) differ.
const cfg = attestationConfig();
const navTabs =
cfg && isConfiguredAttestor(cfg, name.package_address)
? Tabs
: Tabs.filter((t) => t.key !== "issued");

const [activeTab, setActiveTab] = useState(navTabs[0]!.key);

useEffect(() => {
const tab = searchParams.get("tab");
Expand All @@ -112,7 +170,7 @@ export function SinglePackage({
<div className="container">
<div className="grid grid-cols-1 gap-2xl lg:grid-cols-24">
<SinglePackageTabs
tabs={Tabs}
tabs={navTabs}
name={name}
setActiveTab={updateTab}
isActiveTab={isActiveTab}
Expand Down
33 changes: 30 additions & 3 deletions app/src/components/single-package/SinglePackageHeader.tsx

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: need to click the trusted attestor icon twice to get to all attestors page

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[CLAUDE] Fixed — TrustedAttestorBadge used a plain <a href="/attestors"> (a hard full-document navigation), which didn't reliably activate on the first click from the client-rendered package page. Switched to next/link <Link> for single-click client-side navigation.

Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
import Link from "next/link";
import { ResolvedName } from "@/hooks/mvrResolution";
import { Text } from "../ui/Text";
import ImageWithFallback from "../ui/image-with-fallback";
import { beautifySuiAddress } from "@/lib/utils";
import { CopyBtn } from "../ui/CopyBtn";
import { attestationConfig, isConfiguredAttestor } from "@/lib/attestations";
import { ShieldCheckIcon } from "@/icons/single-package/ShieldCheckIcon";

/** Pill marking a package as a trusted attestor in this consumer's trust
* config. Links to the full trusted-attestors list. Uses next/link so the
* first click navigates (a plain <a> hard-navigates and was flaky here). */
function TrustedAttestorBadge() {
return (
<Link
href="/attestors"
className="flex items-center gap-2xs rounded-full bg-bg-quarternaryBleedthrough px-xs py-2xs hover:bg-bg-accentBleedthrough3"
>
<ShieldCheckIcon className="h-3.5 w-3.5 text-content-positive" />
<Text kind="label" size="label-2xs">
MVR-trusted attestor
</Text>
</Link>
);
}

export function SinglePackageHeader({
name,
Expand All @@ -11,6 +31,10 @@ export function SinglePackageHeader({
name: ResolvedName;
network: "mainnet" | "testnet";
}) {
const cfg = attestationConfig();
const isTrustedAttestor =
!!cfg && isConfiguredAttestor(cfg, name.package_address);

return (
<div className="container flex items-center justify-between py-md md:py-lg">
<div className="flex items-center gap-md">
Expand All @@ -20,9 +44,12 @@ export function SinglePackageHeader({
className="h-14 w-14 rounded-sm"
/>
<div className="flex flex-col gap-2xs">
<Text kind="heading" size="heading-regular">
{name.name}
</Text>
<div className="flex items-center gap-sm">
<Text kind="heading" size="heading-regular">
{name.name}
</Text>
{isTrustedAttestor && <TrustedAttestorBadge />}
</div>
<Text
kind="paragraph"
size="paragraph-xs"
Expand Down
Loading
Loading