Skip to content
Open
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
14 changes: 14 additions & 0 deletions .loopover.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -1493,3 +1493,17 @@ settings:
# # To stop trusting a peer, remove its key -- that is the whole revocation story, by design.
# peerKeys:
# - 0000000000000000000000000000000000000000000000000000000000000000

# Public proof page (#9569) — the shareable, unauthenticated per-repo verification page
# (`/proof/<owner>/<repo>`) and its README badge.
#
# OPT-OUT, not opt-in. Every figure the page renders is ALREADY publicly fetchable through the
# ledger-verify, anchors and decision-record endpoints, so gating a page over data anyone can already
# curl would add friction without adding privacy. This block exists because a page is nonetheless a
# different artifact from an API: it is discoverable, linkable, and it markets this repo's numbers.
#
# Precedence: the operator's fleet-wide LOOPOVER_PUBLIC_PROOF flag must be on first. This block can turn
# THIS repo's page OFF; it cannot turn one ON that the operator has not enabled. Omit the block entirely
# to keep the page on once the operator enables it.
# publicProof:
# enabled: false # Bool. Default when the block is absent: true (opt-out).
76 changes: 76 additions & 0 deletions apps/loopover-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -26252,6 +26252,82 @@
}
}
}
},
"/v1/public/repos/{owner}/{repo}/proof": {
"get": {
"operationId": "getPublicRepoProof",
"tags": [
"Public"
],
"summary": "Public proof summary for one repo — ledger status, anchor, calibration with coverage and interval, sample records",
"parameters": [
{
"schema": {
"type": "string"
},
"required": true,
"name": "owner",
"in": "path"
},
{
"schema": {
"type": "string"
},
"required": true,
"name": "repo",
"in": "path"
}
],
"responses": {
"200": {
"description": "ProofSummary. Any accuracy figure carries its coverage AND a Wilson confidence interval; below the sample floor it is an explicit `insufficient_data` state, never a bare percentage. Carries the verification-boundary statement in the payload"
},
"404": {
"description": "The proof page is disabled fleet-wide, or this repo has opted out"
},
"503": {
"description": "Composition failed — no partial or fabricated summary is served"
}
}
}
},
"/v1/public/repos/{owner}/{repo}/proof-badge.svg": {
"get": {
"operationId": "getPublicRepoProofBadge",
"tags": [
"Public"
],
"summary": "README badge for the proof page — reports the ledger's state, never a bare accuracy percentage",
"parameters": [
{
"schema": {
"type": "string"
},
"required": true,
"name": "owner",
"in": "path"
},
{
"schema": {
"type": "string"
},
"required": true,
"name": "repo",
"in": "path"
}
],
"responses": {
"200": {
"description": "SVG badge"
},
"404": {
"description": "Disabled or opted out — still an SVG (a neutral 'unavailable' badge), so a README never shows a broken image"
},
"503": {
"description": "Same neutral SVG on an internal error"
}
}
}
}
},
"servers": [
Expand Down
14 changes: 14 additions & 0 deletions config/examples/loopover.full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1507,3 +1507,17 @@ settings:
# # To stop trusting a peer, remove its key -- that is the whole revocation story, by design.
# peerKeys:
# - 0000000000000000000000000000000000000000000000000000000000000000

# Public proof page (#9569) — the shareable, unauthenticated per-repo verification page
# (`/proof/<owner>/<repo>`) and its README badge.
#
# OPT-OUT, not opt-in. Every figure the page renders is ALREADY publicly fetchable through the
# ledger-verify, anchors and decision-record endpoints, so gating a page over data anyone can already
# curl would add friction without adding privacy. This block exists because a page is nonetheless a
# different artifact from an API: it is discoverable, linkable, and it markets this repo's numbers.
#
# Precedence: the operator's fleet-wide LOOPOVER_PUBLIC_PROOF flag must be on first. This block can turn
# THIS repo's page OFF; it cannot turn one ON that the operator has not enabled. Omit the block entirely
# to keep the page on once the operator enables it.
# publicProof:
# enabled: false # Bool. Default when the block is absent: true (opt-out).
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"lint:composite-actions": "node --experimental-strip-types scripts/lint-composite-actions.ts",
"ui:dev": "npm run ui:preview",
"ui:kit:build": "npm run build --workspace @loopover/ui-kit",
"ui:build": "npm run ui:kit:build && turbo run build --filter=@loopover/engine && npm run ui:openapi && npm --workspace @loopover/ui run build && npm --workspace @loopover/ui-miner run build",
"ui:build": "npm run ui:kit:build && turbo run build --filter=@loopover/engine --filter=@loopover/contract && npm run ui:openapi && npm --workspace @loopover/ui run build && npm --workspace @loopover/ui-miner run build",
"ui:preview": "npm run ui:build && wrangler dev --config apps/loopover-ui/dist/server/wrangler.json --ip 127.0.0.1 --port 4173 --local",
"ui:lint": "npm run ui:kit:build && npm --workspace @loopover/ui-kit run format:check && npm --workspace @loopover/ui run format:check && npm --workspace @loopover/ui run lint && npm --workspace @loopover/ui-miner run format:check && npm --workspace @loopover/ui-miner run lint",
"ui:typecheck": "npm run ui:kit:build && npm --workspace @loopover/ui run typecheck && npm --workspace @loopover/ui-miner run typecheck",
Expand Down
45 changes: 45 additions & 0 deletions packages/loopover-engine/src/focus-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,21 @@ export type FocusManifestPublicStatsConfig = {
enabled: boolean;
};

/**
* Per-repo opt-OUT for the public proof page and its badge (#9569), declared under `publicProof:`.
*
* Shape matches `publicStats:`/`ops:`, but the PRECEDENCE is deliberately different: those are fleet-wide
* and read from the operator's self-repo manifest, whereas this is read from the TARGET repo's own manifest,
* because the thing being opted out of is that repo's own page. Not present ⇒ enabled, once the operator's
* LOOPOVER_PUBLIC_PROOF flag is on — opt-OUT, since every figure the page renders is already publicly
* fetchable through the ledger-verify / anchors / decision-record endpoints. A repo can turn its page off;
* it cannot turn one on that the operator has not enabled.
*/
export type FocusManifestPublicProofConfig = {
present: boolean;
enabled: boolean;
};

/**
* Config-as-code override for the internal, bearer-gated contributor-trust-profile / fairness-analytics
* surface (LOOPOVER_FAIRNESS_ANALYTICS, #fairness-analytics), declared under `fairnessAnalytics:`. Same
Expand Down Expand Up @@ -1249,6 +1264,7 @@ export type FocusManifest = {
maintainerRecap: FocusManifestMaintainerRecapConfig;
ops: FocusManifestOpsConfig;
publicStats: FocusManifestPublicStatsConfig;
publicProof: FocusManifestPublicProofConfig;
fairnessAnalytics: FocusManifestFairnessAnalyticsConfig;
draftFlow: FocusManifestDraftFlowConfig;
upstreamDriftIssues: FocusManifestUpstreamDriftIssuesConfig;
Expand Down Expand Up @@ -1413,6 +1429,13 @@ const EMPTY_PUBLIC_STATS_CONFIG: FocusManifestPublicStatsConfig = {
enabled: false,
};

/** #9569: absent means ENABLED at the resolver (opt-out), so `enabled:false` here is only the shape's
* default — `present:false` is what the resolver actually keys on. */
const EMPTY_PUBLIC_PROOF_CONFIG: FocusManifestPublicProofConfig = {
present: false,
enabled: false,
};

const EMPTY_FAIRNESS_ANALYTICS_CONFIG: FocusManifestFairnessAnalyticsConfig = {
present: false,
enabled: false,
Expand Down Expand Up @@ -1478,6 +1501,7 @@ const EMPTY_MANIFEST: FocusManifest = {
maintainerRecap: { ...EMPTY_MAINTAINER_RECAP_CONFIG },
ops: { ...EMPTY_OPS_CONFIG },
publicStats: { ...EMPTY_PUBLIC_STATS_CONFIG },
publicProof: { ...EMPTY_PUBLIC_PROOF_CONFIG },
fairnessAnalytics: { ...EMPTY_FAIRNESS_ANALYTICS_CONFIG },
draftFlow: { ...EMPTY_DRAFT_FLOW_CONFIG },
upstreamDriftIssues: { ...EMPTY_UPSTREAM_DRIFT_ISSUES_CONFIG },
Expand Down Expand Up @@ -1520,6 +1544,7 @@ function emptyManifest(source: FocusManifestSource, warnings: string[] = []): Fo
maintainerRecap: { ...EMPTY_MAINTAINER_RECAP_CONFIG },
ops: { ...EMPTY_OPS_CONFIG },
publicStats: { ...EMPTY_PUBLIC_STATS_CONFIG },
publicProof: { ...EMPTY_PUBLIC_PROOF_CONFIG },
fairnessAnalytics: { ...EMPTY_FAIRNESS_ANALYTICS_CONFIG },
draftFlow: { ...EMPTY_DRAFT_FLOW_CONFIG },
upstreamDriftIssues: { ...EMPTY_UPSTREAM_DRIFT_ISSUES_CONFIG },
Expand Down Expand Up @@ -2356,6 +2381,24 @@ export function publicStatsConfigToJson(config: FocusManifestPublicStatsConfig):
return { enabled: config.enabled };
}

/** Parse the optional `publicProof:` mapping (#9569). Mirrors {@link parsePublicStatsConfig} exactly. */
function parsePublicProofConfig(value: JsonValue | undefined, warnings: string[]): FocusManifestPublicProofConfig {
if (value === undefined || value === null) return { ...EMPTY_PUBLIC_PROOF_CONFIG };
if (typeof value !== "object" || Array.isArray(value)) {
warnings.push('Manifest field "publicProof" must be a mapping; ignoring it.');
return { ...EMPTY_PUBLIC_PROOF_CONFIG };
}
const record = value as Record<string, JsonValue>;
const enabled = normalizeOptionalBoolean(record.enabled, "publicProof.enabled", warnings) ?? false;
return { present: true, enabled };
}

/** Serialize a publicProof config so a cached snapshot round-trips through {@link parsePublicProofConfig}. */
export function publicProofConfigToJson(config: FocusManifestPublicProofConfig): JsonValue {
if (!config.present) return null;
return { enabled: config.enabled };
}

/** Parse the optional `fairnessAnalytics:` mapping (#fairness-analytics). Mirrors {@link parsePublicStatsConfig}
* exactly -- the only field is `enabled`, no DB layer to overlay onto. */
function parseFairnessAnalyticsConfig(value: JsonValue | undefined, warnings: string[]): FocusManifestFairnessAnalyticsConfig {
Expand Down Expand Up @@ -4247,6 +4290,7 @@ export const FOCUS_MANIFEST_TOP_LEVEL_FIELDS = [
"maintainerRecap",
"ops",
"publicStats",
"publicProof",
"draftFlow",
"upstreamDriftIssues",
"sweepWatchdog",
Expand Down Expand Up @@ -4326,6 +4370,7 @@ export function parseFocusManifest(raw: unknown, source?: FocusManifestSource):
maintainerRecap: parseMaintainerRecapConfig(record.maintainerRecap, warnings),
ops: parseOpsConfig(record.ops, warnings),
publicStats: parsePublicStatsConfig(record.publicStats, warnings),
publicProof: parsePublicProofConfig(record.publicProof, warnings),
fairnessAnalytics: parseFairnessAnalyticsConfig(record.fairnessAnalytics, warnings),
draftFlow: parseDraftFlowConfig(record.draftFlow, warnings),
upstreamDriftIssues: parseUpstreamDriftIssuesConfig(record.upstreamDriftIssues, warnings),
Expand Down
39 changes: 39 additions & 0 deletions src/api/proof-badge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// README badge for the public proof page (#9569). Renders through the SAME flat-badge primitive and the
// SAME XML escaping as the repo-quality badge (`./badge.ts`), so an unauthenticated, embeddable surface
// cannot become an injection vector and the two badges cannot drift apart visually.
import { escapeXml } from "./badge";
import { buildProofBadgeColor, buildProofBadgeMessage, type ProofSummary } from "../review/proof-summary";

export const PROOF_BADGE_LABEL = "loopover proof";
const UNAVAILABLE_COLOR = "#9e9e9e";

/** `null` renders the neutral unavailable badge — used for both the flag-off 404 and the error 503, since
* from a README's point of view those are the same thing: no claim is being made right now. */
export function renderProofBadgeSvg(summary: ProofSummary | null): string {
const message = summary ? buildProofBadgeMessage(summary) : "unavailable";
const color = summary ? buildProofBadgeColor(summary) : UNAVAILABLE_COLOR;
return renderFlatBadge(PROOF_BADGE_LABEL, message, color);
}

function renderFlatBadge(label: string, message: string, color: string): string {
const labelText = escapeXml(label);
const messageText = escapeXml(message);
const labelWidth = textWidth(label);
const messageWidth = textWidth(message);
const totalWidth = labelWidth + messageWidth;
return [
`<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="20" role="img" aria-label="${labelText}: ${messageText}">`,
`<title>${labelText}: ${messageText}</title>`,
`<rect width="${totalWidth}" height="20" rx="3" fill="#fff"/>`,
`<rect width="${labelWidth}" height="20" rx="3" fill="#24292f"/>`,
`<rect x="${labelWidth}" width="${messageWidth}" height="20" rx="3" fill="${escapeXml(color)}"/>`,
`<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">`,
`<text x="${labelWidth / 2}" y="14">${labelText}</text>`,
`<text x="${labelWidth + messageWidth / 2}" y="14">${messageText}</text>`,
`</g></svg>`,
].join("");
}

function textWidth(text: string): number {
return Math.max(40, Math.round(text.length * 6.5) + 10);
}
37 changes: 37 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ import { isRagEnabled } from "../review/rag-wire";
import { loadDecisionLedgerTip, loadPublicDecisionRecord, loadPublicLedgerRow, verifyDecisionLedger } from "../review/decision-record";
import { buildEvalScoreRecordsFromRulePrecision, filterEvalScoreRecords } from "../review/eval-score-records";
import { anchorSigningInput, buildLedgerAnchorPayload, currentAnchorKey, parseAnchorPublicKeys, signLedgerAnchorPayload } from "../review/ledger-anchor";
import { resolveProofPage } from "../review/proof-summary";
import { renderProofBadgeSvg } from "./proof-badge";
import { ingestBittensorAnchorReport, parseBittensorAnchorReport } from "../review/ledger-anchor-bittensor";
import { loadPublicLedgerAnchors } from "../review/ledger-anchor-persistence";
import { getPublicStats, isPublicStatsEnabled, resolvePublicStatsManifestOverride } from "../review/public-stats";
Expand Down Expand Up @@ -1350,6 +1352,41 @@ export function createApp() {
return c.json(result);
});

// #9569: the public proof page's data, and its README badge. Read-only over the SAME public sources the
// standalone endpoints already serve -- no new verification mechanism and no new SQL surface.
//
// Both handlers are thin renderers over ONE resolver (resolveProofPage): the gate, the read and the
// failure outcome are decided there, so the page and the badge cannot disagree about whether a repo is
// published. That is not a stylistic preference -- the gate previously lived inline in both bodies and
// exactly one of them was wired to the per-repo opt-out.
const proofPageDeps = {
loadManifest: loadRepoFocusManifest,
verifyLedger: (env: Env) => verifyDecisionLedger(env),
loadAnchors: (env: Env) => loadPublicLedgerAnchors(env, { limit: 20 }),
};

app.get("/v1/public/repos/:owner/:repo/proof", async (c) => {
const result = await resolveProofPage(c.env, `${c.req.param("owner")}/${c.req.param("repo")}`, proofPageDeps);
if (result.status === "disabled") return c.json({ error: "not_found" }, 404);
c.header("Cache-Control", "public, max-age=60, stale-while-revalidate=300");
return c.json(result.summary);
});

// The badge deliberately reports the LEDGER's state rather than an accuracy percentage: a badge is a
// one-glance claim, and an accuracy number without the interval that makes it honest (which does not fit
// in a badge) is exactly the bare scalar the proof summary refuses to publish. A disabled repo renders
// the neutral badge rather than an error -- a broken image in a README is worse than an honest one.
app.get("/v1/public/repos/:owner/:repo/proof-badge.svg", async (c) => {
c.header("Content-Type", "image/svg+xml; charset=utf-8");
const result = await resolveProofPage(c.env, `${c.req.param("owner")}/${c.req.param("repo")}`, proofPageDeps);
if (result.status === "disabled") {
c.header("Cache-Control", "public, max-age=300");
return c.body(renderProofBadgeSvg(null), 404);
}
c.header("Cache-Control", "public, max-age=600, stale-while-revalidate=86400");
return c.body(renderProofBadgeSvg(result.summary));
});

// #9277 (epic #9267): the current tip's SIGNED checkpoint, for the operator's off-Worker Bittensor
// commitment submitter to fetch and commit on-chain (sha256 of `signingInput` is the exact 32 bytes
// `Data::Sha256` holds). Unauthenticated like every /v1/public/* sibling: it is the same payload the
Expand Down
5 changes: 5 additions & 0 deletions src/auth/route-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export function requiresApiToken(path: string): boolean {
if (path === "/v1/public/decision-ledger/anchor-key") return false;
// #9271: the public anchor-attempt listing, added in the SAME PR as its route.
if (path === "/v1/public/decision-ledger/anchors") return false;
// #9569: the public proof page's data and its README badge. Unauthenticated by design -- every figure
// they render is already served unauthenticated by the ledger-verify / anchors / decision-record routes
// above; this is a composition, not a new disclosure. Added in the SAME PR as the routes, per #9120.
if (/^\/v1\/public\/repos\/[^/]+\/[^/]+\/proof$/.test(path)) return false;
if (/^\/v1\/public\/repos\/[^/]+\/[^/]+\/proof-badge\.svg$/.test(path)) return false;
// #9277: the current tip's signed checkpoint, for the operator's off-Worker Bittensor submitter (and
// anyone else — it is the same payload the Rekor/git backends already publish externally). Added in the
// SAME PR as its route, per the #9120 lesson.
Expand Down
4 changes: 4 additions & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,10 @@ declare global {
* worker is byte-identical to today. Exposes review-disposition counts + a reversal-grounded accuracy
* percentage + an estimated-time-saved figure ONLY — never PR content, authors, scores, or reward internals.
* See review/public-stats.ts. */
/** #9569: fleet-wide switch for the public proof page (`/proof/:owner/:repo`) and its badge. Default
* OFF like every sibling public surface. A repo can opt OUT via its own manifest, but cannot opt IN
* when this is off -- see isProofPageEnabledForRepo's recorded decision in review/proof-summary.ts. */
LOOPOVER_PUBLIC_PROOF?: string;
LOOPOVER_PUBLIC_STATS?: string;
/** Proof of Power (#1059): comma-separated allowlist of repo full-names ("owner/repo") whose OWN historical
* review ledger (audit_events "published a review surface" + pull_requests terminal state) counts toward
Expand Down
Loading
Loading