Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { inspectServicePath } from "@/src/catalog/inspection";
import { isCounterpartyEvidenceDemoListing } from "@/src/catalog/counterpartyEvidence";
import { loadCatalog } from "@/src/catalog/store";
import { safeJsonLd } from "@/src/components/structuredData";
import { safePublicEndpoint } from "@/src/catalog/publicEndpoint";
import { directoryEvidenceState } from "@/src/components/directory-evidence-state";

export const dynamic = "force-dynamic";

Expand Down Expand Up @@ -45,22 +45,18 @@ export default async function ServicePage({ params }: { params: Promise<Params>
const found = findService(sellerClaim, listingId, version);
if (!found) notFound();
const { seller, listing } = found;
const identity = tierMeta(seller.identityTier ?? "self-declared");
const evidence = directoryEvidenceState(listing, seller);
const identity = tierMeta(evidence.identityTier);
const apiHref = `/api/dacs/listings/${encodeURIComponent(listing.listingId)}/${listing.version}?seller=${encodeURIComponent(seller.primaryClaim)}`;
const inspectHref = inspectServicePath(listing);
const engagementEndpoint = safePublicEndpoint(listing.publicEndpoint);
const engagementEndpoint = evidence.endpoint.url;
const isCounterpartyEvidenceDemo = isCounterpartyEvidenceDemoListing(seller.primaryClaim, listing);
const isFixtureListing = listing.anchor.kind === "fixture";
const listingProfileLabel = isFixtureListing
? "fixture listing"
: listing.artifactProfile === "dacs-v0.1"
? "current DACS listing"
: "legacy SDK listing";
const technicalArtifactProfile = isFixtureListing ? "fixture-listing" : (listing.artifactProfile ?? "legacy-sdk-v0.1");
const listingArtifactLabel = isFixtureListing ? "View fixture contract" : "View signed listing artifact";
const noEndpointNote = isFixtureListing
? "This fixture has no live engagement endpoint. Inspect the fixture contract before coordinating off-directory."
: "This seller has not published a safe HTTPS engagement endpoint. Inspect the signed artifact before coordinating off-directory.";
const endpointDeclarationNote = isFixtureListing && evidence.endpoint.kind === "not-declared"
? "This fixture declares no live engagement endpoint."
: `${evidence.endpoint.explanation}.`;
const structuredData = {
"@context": "https://schema.org",
"@type": "Service",
Expand All @@ -86,7 +82,7 @@ export default async function ServicePage({ params }: { params: Promise<Params>
<div className="service-provider-row">
<span>Offered by <Link className="text-link" href={`/seller/${encodeURIComponent(seller.primaryClaim)}`}>{seller.displayName}</Link></span>
<span className={`badge ${identity.chipClass}`}>{identity.label}</span>
<span className={`badge ${listing.artifactProfile === "dacs-v0.1" ? "ok" : ""}`}>{listingProfileLabel}</span>
<span className={`badge ${evidence.listing.kind === "current" ? "ok" : ""}`}>{evidence.listing.label}</span>
{seller.ownerRegistered && <span className="badge ok">owner-registered</span>}
{!isFixtureListing && !seller.ownerRegistered && seller.discovered && <span className="badge">discovered on-chain</span>}
{isFixtureListing && <span className="badge">not chain anchored</span>}
Expand All @@ -102,7 +98,10 @@ export default async function ServicePage({ params }: { params: Promise<Params>
<a className="btn secondary" href={inspectHref}>Inspect service</a>
<Link className="btn secondary" href={`/seller/${encodeURIComponent(seller.primaryClaim)}`}>View seller evidence</Link>
</div>
<p className="note">{engagementEndpoint ? "The HTTPS endpoint is advertised inside the signed listing; it is a contact route, not a cryptographic trust anchor." : noEndpointNote}</p>
<p className="note">
{endpointDeclarationNote} <strong>Reachability:</strong> {evidence.reachability.label}.
{engagementEndpoint && " A declared contact route is not a health check or cryptographic trust anchor."}
</p>
</section>

<div className="service-layout">
Expand All @@ -120,11 +119,13 @@ export default async function ServicePage({ params }: { params: Promise<Params>

<aside className="card trust-card" aria-labelledby="trust-heading">
<div className="eyebrow">trust at a glance</div>
<h2 id="trust-heading" className="card-section-title">Three different checks</h2>
<h2 id="trust-heading" className="card-section-title">Evidence boundaries</h2>
<ul className="trust-checks">
<li><span className={seller.cci.length ? "check ok" : "check"}>{seller.cci.length ? "✓" : "–"}</span><div><strong>Identity links</strong><p>{seller.cci.length ? `${seller.cci.length} GCR identity link${seller.cci.length === 1 ? "" : "s"}; no fresh DACS-2 verification resolved` : "No linked identities beyond the signing key"}</p></div></li>
<li><span className={isFixtureListing ? "check" : "check ok"}>{isFixtureListing ? "–" : "✓"}</span><div><strong>Listing</strong><p>{isFixtureListing ? "Fixture machine contract and content hash match; no chain anchor claimed" : "Signature and chain anchor verified"}</p></div></li>
<li><span className={seller.reputation.completed ? "check ok" : "check"}>{seller.reputation.completed ? "✓" : "–"}</span><div><strong>Two-sided deal evidence</strong><p>{seller.reputation.completed}/{seller.reputation.totalAgreements} completed bundles passed strict verification</p></div></li>
<li><span className="check">{engagementEndpoint ? "•" : "–"}</span><div><strong>Endpoint declaration</strong><p>{evidence.endpoint.label}; this says nothing about availability</p></div></li>
<li><span className="check">–</span><div><strong>Reachability</strong><p>{evidence.reachability.label}</p></div></li>
<li><span className={evidence.deals.completed ? "check ok" : "check"}>{evidence.deals.completed ? "✓" : "–"}</span><div><strong>Two-sided deal evidence</strong><p>{evidence.deals.label}. {evidence.deals.explanation}.</p></div></li>
</ul>
</aside>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { activeCatalogSellers } from "@/src/catalog/discovery";
import type { ListingSummary, SellerRecord } from "@/src/catalog/types";
import { directoryEvidenceState } from "./directory-evidence-state";
import { deliveryLabel, pricingModelLabel, publishedPriceLabel, railLabel, tierMeta } from "./labels";

const sellerTier = (seller: SellerRecord) => seller.identityTier ?? "self-declared";
Expand All @@ -14,13 +15,6 @@ const sellerTier = (seller: SellerRecord) => seller.identityTier ?? "self-declar
const provenance = (seller: SellerRecord) =>
seller.ownerRegistered ? "owner-registered" : seller.discovered ? "found on-chain" : "unverified submission";

const listingTrustLabel = (listing: ListingSummary) =>
listing.anchor.kind === "fixture"
? "fixture listing"
: listing.artifactProfile === "dacs-v0.1"
? "current DACS listing"
: "legacy SDK listing";

const listingProvenance = (seller: SellerRecord, listing: ListingSummary) =>
listing.anchor.kind === "fixture" ? "fixture catalog" : provenance(seller);

Expand Down Expand Up @@ -213,14 +207,15 @@ export default function DirectoryExplorer({ sellers, indexed }: { sellers: Selle

<div className="service-grid">
{filtered.map(({ listing, seller }) => {
const trust = tierMeta(sellerTier(seller));
const evidence = directoryEvidenceState(listing, seller);
const trust = tierMeta(evidence.identityTier);
const href = `/service/${encodeURIComponent(seller.primaryClaim)}/${encodeURIComponent(listing.listingId)}/${listing.version}`;
const jsonHref = `/api/dacs/listings/${encodeURIComponent(listing.listingId)}/${listing.version}?seller=${encodeURIComponent(seller.primaryClaim)}`;
return (
<article key={`${seller.primaryClaim}/${listing.listingId}/${listing.version}`} className="card service-card">
<div className="service-card-topline">
<span className="eyebrow">{listing.offering.category.replaceAll(".", " / ")}</span>
<span className={`badge ${listing.artifactProfile === "dacs-v0.1" ? "ok" : ""}`}>{listingTrustLabel(listing)}</span>
<span className={`badge ${evidence.listing.kind === "current" ? "ok" : ""}`}>{evidence.listing.label}</span>
</div>
<h3><Link href={href} className="card-title-link">{listing.offering.title}</Link></h3>
<p className="byline">
Expand All @@ -231,6 +226,8 @@ export default function DirectoryExplorer({ sellers, indexed }: { sellers: Selle
<div className="service-facts">
<div><span>pricing</span><strong>{listing.pricing.priceHint ? publishedPriceLabel(listing.pricing) : pricingModelLabel(listing.pricing, listing.offering.negotiation)}</strong></div>
<div><span>delivery</span><strong>{listing.offering.delivery?.[0] ? deliveryLabel(listing.offering.delivery[0]) : "Not stated"}</strong></div>
<div><span>endpoint</span><strong>{evidence.endpoint.label}</strong></div>
<div><span>reachability</span><strong>{evidence.reachability.label}</strong></div>
</div>
<div className="card-meta">
<span className="meta-label">identity</span>
Expand All @@ -242,7 +239,7 @@ export default function DirectoryExplorer({ sellers, indexed }: { sellers: Selle
</span>
<span className="meta-label">record</span>
<span className="meta-chips"><span className={seller.reputation.completed ? "badge ok" : "badge"}>
{seller.reputation.completed}/{seller.reputation.totalAgreements} strict bundles
{evidence.deals.label}
</span></span>
</div>
<div className="service-actions">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { safePublicEndpoint } from "../catalog/publicEndpoint.js";
import type { IdentityTier, ListingSummary, SellerRecord } from "../catalog/types.js";

export const UNMEASURED_REACHABILITY_LABEL = "Not measured by Directory";

export type DirectoryEvidenceState = Readonly<{
listing: Readonly<{
kind: "fixture" | "current" | "legacy";
label: "fixture listing" | "current DACS listing" | "legacy SDK listing";
}>;
endpoint: Readonly<{
kind: "declared" | "not-declared" | "unsafe-declaration";
label: "Declared HTTPS endpoint" | "No endpoint declared" | "Endpoint declaration not shown";
explanation: string;
url?: string;
}>;
reachability: Readonly<{
kind: "not-measured";
label: typeof UNMEASURED_REACHABILITY_LABEL;
}>;
identityTier: IdentityTier;
deals: Readonly<{
completed: number;
total: number;
label: string;
explanation: string;
}>;
}>;

export function directoryEvidenceState(
listing: ListingSummary,
seller: Pick<SellerRecord, "identityTier" | "reputation">,
): DirectoryEvidenceState {
const endpoint = safePublicEndpoint(listing.publicEndpoint);
const hasEndpointDeclaration = typeof listing.publicEndpoint === "string" && listing.publicEndpoint.length > 0;
const listingState = listing.anchor.kind === "fixture"
? { kind: "fixture" as const, label: "fixture listing" as const }
: listing.artifactProfile === "dacs-v0.1"
? { kind: "current" as const, label: "current DACS listing" as const }
: { kind: "legacy" as const, label: "legacy SDK listing" as const };
const completed = seller.reputation.completed;
const total = seller.reputation.totalAgreements;

return Object.freeze({
listing: Object.freeze(listingState),
endpoint: Object.freeze(endpoint
? {
kind: "declared" as const,
label: "Declared HTTPS endpoint" as const,
explanation: "A safe HTTPS contact route is declared in the listing",
url: endpoint,
}
: hasEndpointDeclaration
? {
kind: "unsafe-declaration" as const,
label: "Endpoint declaration not shown" as const,
explanation: "The declared endpoint is not a safe HTTPS URL and is not linked",
}
: {
kind: "not-declared" as const,
label: "No endpoint declared" as const,
explanation: "The listing declares no engagement endpoint",
}),
reachability: Object.freeze({ kind: "not-measured" as const, label: UNMEASURED_REACHABILITY_LABEL }),
identityTier: seller.identityTier ?? "self-declared",
deals: Object.freeze({
completed,
total,
label: `${completed} strict-verified / ${total} agreement${total === 1 ? "" : "s"}`,
explanation: total === 0
? "No verified two-sided bundle history yet"
: "Both parties' signed copies must verify and agree before a bundle counts",
}),
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import assert from "node:assert/strict";
import test from "node:test";
import { directoryEvidenceState, UNMEASURED_REACHABILITY_LABEL } from "../src/components/directory-evidence-state.js";
import type { ListingSummary, SellerRecord } from "../src/catalog/types.js";

const listing = (overrides: Partial<ListingSummary> = {}): ListingSummary => ({
listingId: "service",
version: 1,
contentHash: "a".repeat(64),
anchor: { kind: "storage-program", locator: "stor-listing" },
seller: { primaryClaim: "key:seller", displayName: "Seller" },
artifactProfile: "dacs-v0.1",
offering: { title: "Service", category: "services.other", tags: [] },
pricing: {},
status: "active",
catalogObservedAt: 1,
...overrides,
});

const seller = (completed = 0, totalAgreements = 0): Pick<SellerRecord, "identityTier" | "reputation"> => ({
identityTier: "verified",
reputation: { completed, totalAgreements, completionRate: totalAgreements ? completed / totalAgreements : null },
});

test("separates listing evidence, endpoint declaration, and unmeasured reachability", () => {
const state = directoryEvidenceState(listing({ publicEndpoint: "https://agent.example/jobs" }), seller(2, 3));
assert.deepEqual(state.listing, { kind: "current", label: "current DACS listing" });
assert.deepEqual(state.endpoint, {
kind: "declared",
label: "Declared HTTPS endpoint",
explanation: "A safe HTTPS contact route is declared in the listing",
url: "https://agent.example/jobs",
});
assert.deepEqual(state.reachability, { kind: "not-measured", label: UNMEASURED_REACHABILITY_LABEL });
assert.equal(state.identityTier, "verified");
assert.equal(state.deals.label, "2 strict-verified / 3 agreements");
assert.match(state.deals.explanation, /Both parties/);
});

test("does not turn a missing endpoint into a service failure", () => {
const state = directoryEvidenceState(listing({ publicEndpoint: undefined }), seller());
assert.deepEqual(state.endpoint, {
kind: "not-declared",
label: "No endpoint declared",
explanation: "The listing declares no engagement endpoint",
});
assert.equal(state.reachability.label, "Not measured by Directory");
assert.equal(state.deals.label, "0 strict-verified / 0 agreements");
assert.equal(state.deals.explanation, "No verified two-sided bundle history yet");
});

test("distinguishes unsafe declarations without exposing or linking them", () => {
for (const publicEndpoint of ["http://agent.example", "https://alice@agent.example"]) {
const state = directoryEvidenceState(listing({ publicEndpoint }), seller());
assert.deepEqual(state.endpoint, {
kind: "unsafe-declaration",
label: "Endpoint declaration not shown",
explanation: "The declared endpoint is not a safe HTTPS URL and is not linked",
});
assert.equal(state.reachability.label, "Not measured by Directory");
}
});

test("keeps fixture and legacy listing evidence visibly distinct", () => {
assert.deepEqual(
directoryEvidenceState(listing({ anchor: { kind: "fixture", locator: "fixture://service" }, artifactProfile: "fixture-listing" }), seller()).listing,
{ kind: "fixture", label: "fixture listing" },
);
assert.deepEqual(
directoryEvidenceState(listing({ artifactProfile: "legacy-sdk-v0.1" }), seller()).listing,
{ kind: "legacy", label: "legacy SDK listing" },
);
});