Merge remote-tracking branch 'origin/master' into openprofile-broadcast-guest

# Conflicts:
#	apps/logicsrc-web/src/app/llms.txt/route.ts
This commit is contained in:
Anthony Ettinger 2026-09-13 03:30:35 +00:00
commit a36fe5da52
8 changed files with 882 additions and 0 deletions

View file

@ -32,6 +32,8 @@ export function GET(): Response {
- [OpenDisk](${SITE_URL}/opendisk): One file a machine serves about the disk it will rent: free GiB, price per GiB-month, location, policy, proof cadence and hub standing, discovered at /.well-known/opendisk.json. What a peer-to-peer storage market is made of; reference marketplace d1sks.com.
- [OpenCoupon](${SITE_URL}/opencoupon): One file a merchant serves about what is on offer right now, at /.well-known/opencoupon.json: every code, sale and shipping threshold with kind, value, scope, dates, status and regions, expired codes kept so directories learn they died. A coupon site reads the merchant instead of a forum thread.
- [OpenRecipe.md](${SITE_URL}/openrecipe): One Markdown file that is a recipe: summary block (Serves, Prep, Cook, Cuisine, Diet, Author, Source), ingredients and steps as written, notes, nutrition; served next to the page or linked with rel="openrecipe"; schema.org/Recipe JSON-LD is derived from it, never the reverse.
- [OpenAffiliate](${SITE_URL}/openaffiliate): One file a merchant serves about the commission it pays, at /.well-known/openaffiliate.json: programs with what pays (sale, subscription, signup, lead, install), percent or amount, attribution window, hold days and payout methods; four calls let a person or an agent join with an OpenProfile.md, link with ?oa=code, read its own ledger and get paid to its own address. No network in the money; reference implementation crawlproof.com/affiliate.
- [OpenThreat](${SITE_URL}/openthreat): One file a security tool serves about what it found in the open, at /.well-known/openthreat.json: findings in public repositories, attacks on the reporter's own infrastructure, indicators and advisories, with severity, rule, subject and status. Private subjects are never in it, secrets are never located while open, announcing is on by default with a one-switch opt-out. First reporter threatcrush.com/discovery, first directory nichedb.dev/c/threats.
- [OpenBroadcast](${SITE_URL}/openbroadcast): The Broadcast section of an OpenProfile.md: the show a person hosts (podcast, radio, live audio, stream) with kind, format, cadence, audience, topics, slots, whether it pays or charges guests, and who the host is seeking, so a host and a guest are matched from two files rather than two forms.
- [OpenGuest](${SITE_URL}/openguest): The Guest section of an OpenProfile.md: that a person will appear on shows, with expertise, credentials, pitch, formats, availability, rate, past appearances and dealbreakers. An expert is a guest with Expertise and Credentials. Matched against OpenBroadcast.
- [AgentSwarm](${SITE_URL}/agent-swarm): Provider-neutral agent orchestration, model routing, and cost controls.

View file

@ -0,0 +1,233 @@
import Link from "next/link";
import type { ReactNode } from "react";
import type { Metadata } from "next";
import { SiteShell } from "@/components/site-shell";
import { mono, pre, table, td, th } from "../openontology/ui";
export const metadata: Metadata = {
title: "OpenAffiliate · LogicSRC",
description:
"OpenAffiliate is one file a merchant serves about the commission it pays, at /.well-known/openaffiliate.json, and four calls that let a person or an agent earn it: join with a profile, link with one parameter, read your own ledger, get paid to your own address. No network in the money.",
alternates: { canonical: "/openaffiliate" }
};
const DESCRIPTOR = `{
"merchant": { "name": "CrawlProof", "web": "https://crawlproof.com", "currency": "USD",
"terms": "https://crawlproof.com/affiliate/terms" },
"updated": "2026-09-13T06:00:00Z",
"programs": [
{ "id": "partners", "title": "CrawlProof partner program",
"join": "https://crawlproof.com/api/affiliate/v1/join",
"ledger": "https://crawlproof.com/api/affiliate/v1/ledger",
"approval": "open",
"pays": [
{ "event": "sale", "kind": "percent", "value": 30 },
{ "event": "subscription", "kind": "percent", "value": 30, "months": 12 },
{ "event": "signup", "kind": "amount", "value": 0.5 }
],
"link": { "param": "oa", "deep": true },
"window": 30, "attribution": "last", "hold_days": 30,
"payout": { "methods": ["usdc/eip155:137"], "min": 10, "schedule": "weekly" },
"self": "refused", "status": "active" }
]
}`;
const JOIN = `POST https://crawlproof.com/api/affiliate/v1/join
{ "profile": "https://anthony.example/.well-known/openprofile.md" }
201 { "membership": "am_8f3c", "status": "active", "code": "anthony",
"link": "https://crawlproof.com/?oa=anthony", "token": "oa_5Kq…",
"ledger": "https://crawlproof.com/api/affiliate/v1/ledger" }`;
const EVENTS: Array<[string, string, string]> = [
["sale", "a one-time charge", "percent of amount, or a flat amount"],
["subscription", "each charge of a recurring one", "months caps how many renewals pay; absent is every one"],
["signup", "an account created", "usually a flat amount"],
["lead", "a form submitted", "a flat amount"],
["install", "an app installed", "a flat amount"],
["other", "the merchant's own words", "kept, listed, not filtered on"]
];
const CALLS: Array<[string, string]> = [
["Join", "POST the program's join URL with an OpenProfile.md URL. Back comes a code, a link, a token and the terms as they stood. open answers active at once; review answers pending."],
["Link", "The merchant's URL with ?oa=code. No redirect host, no shortener, no pixel. deep: true means it works on any page, so you link to the product you recommend."],
["Ledger", "GET the ledger with the token: clicks, every conversion with its status and hold, balances pending, approved and paid, every payout with its tx. A reversal must carry a reason."],
["Payout", "The whole approved balance to your own pay address on the schedule, once it passes min. Nothing netted, because there is no network to pay."]
];
const NETWORK: Array<[string, string]> = [
["A third of the commission", "Nothing. The merchant pays the affiliate. A directory that charges does so as a stated fee, never as a share of a conversion."],
["Apply, wait weeks", "A profile. approval: open answers at once; review says the merchant looks first, and says so in the file."],
["Terms in a PDF you signed once", "Terms in a file at a fixed URL, fetched any time, kept with the membership as they stood at the join, changed forward only."],
["A reversal with no reason", "A reversal without a reason is not a reversal. The affiliate reports it and a directory says so beside the program."],
["Ten dashboards", "One ledger shape per program, readable by anyone with the token, so one page can show all of them."],
["Sixty days, minus a fee, in their currency", "hold_days, then the schedule, to your own address, in the asset the file names. The tx is in the ledger."]
];
const ABSENT: Array<[string, string]> = [
["No network", "The merchant serves the terms, records the conversions and sends the money. Nobody sits in the middle of a payment."],
["No tracking host", "A link is the merchant's URL with a parameter. Only a navigation sets attribution; an image, frame, script or prefetch sets nothing."],
["No application form", "An affiliate is a profile. A person or an agent, with an operator who answers for the agent."],
["No exclusivity", "A membership binds nobody to one program, and a program may not require it."],
["No impression payments", "A view is not an event. The events are things a customer did."]
];
export default function OpenAffiliatePage(): ReactNode {
return (
<SiteShell active="OpenAffiliate">
<div className="band">
<div className="section-head">
<p className="eyebrow">LogicSRC standards surface</p>
<h2>OpenAffiliate</h2>
<p>
One file a merchant serves about the commission it pays, and four calls that let a
person or an agent earn it. No network in the money.
</p>
</div>
<p style={{ color: "#41505d" }}>
Affiliate marketing runs through networks, and the networks are the problem. A merchant
pays a third of every commission for a pixel and a payout file. An affiliate applies to
each program by hand, waits weeks, and ends up with ten dashboards that disagree. Terms
live in a PDF signed once. A conversion is reversed with no reason. A payout arrives sixty
days later, minus a fee. And none of it is readable by a machine, so an agent that could
earn by recommending the right product cannot find out what the commission is. The
merchant already knows what it pays and what each sale was worth. OpenAffiliate is that,
written down, at <code style={mono}>/.well-known/openaffiliate.json</code>.
</p>
<p style={{ color: "#5b6b7a" }}>
Status: 0.1. The reference implementation is{" "}
<a href="https://crawlproof.com/affiliate">crawlproof.com</a>, which runs its own
program, joins other merchants&apos; programs from the same dashboard, and pays in USDC on
Polygon. The smallest valid file is a merchant with a name and a program with a title
and one thing it pays.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>The descriptor</h2>
<p>
A commission fetched from the merchant&apos;s own origin is the commission the merchant
says it pays, today, in words it cannot say it never agreed to.
</p>
</div>
<pre style={pre}>{DESCRIPTOR}</pre>
<p style={{ color: "#41505d" }}>
<code style={mono}>pays</code> is one entry per event. <code style={mono}>window</code>{" "}
is the attribution window in days and <code style={mono}>attribution</code> whether a
later click replaces an earlier one. <code style={mono}>hold_days</code> is the refund
window a conversion waits out as pending. <code style={mono}>payout.methods</code> are{" "}
<code style={mono}>asset/chain</code> pairs or a named rail. <code style={mono}>self</code>{" "}
says whether the affiliate&apos;s own purchase pays. Unknown keys are kept.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>Four calls</h2>
</div>
<table style={table}>
<tbody>
{CALLS.map(([what, how]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{how}</td>
</tr>
))}
</tbody>
</table>
<pre style={pre}>{JOIN}</pre>
</div>
<div className="band">
<div className="section-head">
<h2>Six events</h2>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>event</th>
<th style={th}>what happened</th>
<th style={th}>how it pays</th>
</tr>
</thead>
<tbody>
{EVENTS.map(([event, what, how]) => (
<tr key={event}>
<td style={td}>
<code style={mono}>{event}</code>
</td>
<td style={td}>{what}</td>
<td style={td}>{how}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>What a network costs, and what replaces it</h2>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>on a network</th>
<th style={th}>with OpenAffiliate</th>
</tr>
</thead>
<tbody>
{NETWORK.map(([before, after]) => (
<tr key={before}>
<td style={td}>{before}</td>
<td style={td}>{after}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>What is deliberately absent</h2>
</div>
<table style={table}>
<tbody>
{ABSENT.map(([what, why]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{why}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>Where everything lives</h2>
</div>
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
<li>
<Link href="/docs/openaffiliate">Specification</Link>: the descriptor, eleven rules,
six events, join, links and attribution, the ledger, webhooks, payouts, discovery,
what a directory owes a merchant
</li>
<li>
<a href="https://crawlproof.com/affiliate">crawlproof.com/affiliate</a>: the reference
implementation, running its own program and a directory of others
</li>
<li>
<Link href="/openprofile">OpenProfile.md</Link>, the affiliate&apos;s identity and pay
address; <Link href="/opencoupon">OpenCoupon</Link>, a merchant&apos;s promotions in
the same shape
</li>
</ul>
</div>
</SiteShell>
);
}

View file

@ -0,0 +1,196 @@
import Link from "next/link";
import type { ReactNode } from "react";
import type { Metadata } from "next";
import { SiteShell } from "@/components/site-shell";
import { mono, pre, table, td, th } from "../openontology/ui";
export const metadata: Metadata = {
title: "OpenThreat · LogicSRC",
description:
"OpenThreat is one file a security tool serves about what it found in the open, at /.well-known/openthreat.json: findings in public repositories, attacks on the reporter's own infrastructure, indicators and advisories, with severity, rule, subject and status. Private subjects are never in it and secrets are never located while open.",
alternates: { canonical: "/openthreat" }
};
const DESCRIPTOR = `{
"openthreat": "0.1",
"reporter": { "name": "ThreatCrush", "web": "https://threatcrush.com", "tool": "threatcrush",
"policy": "https://threatcrush.com/discovery#policy" },
"updated": "2026-09-13T06:00:00Z",
"threats": [
{ "id": "3f9a1c2b", "kind": "finding", "title": "SQL assembled by concatenation",
"severity": "high", "rule": "js-sql-string-building", "cwe": "CWE-89", "category": "code",
"subject": { "name": "northwind/api", "url": "https://github.com/northwind/api", "ref": "main" },
"location": { "file": "src/db/users.ts", "line": 42 },
"status": "open", "last_seen": "2026-09-13T05:40:00Z" },
{ "id": "b71e0d44", "kind": "finding", "title": "Hardcoded credential",
"severity": "critical", "rule": "secret-generic-credential", "cwe": "CWE-798", "category": "secret",
"subject": { "name": "northwind/api", "url": "https://github.com/northwind/api" },
"status": "open" },
{ "id": "ssh-91.232.105.3", "kind": "attack", "title": "SSH brute force", "severity": "medium",
"source": { "ip": "91.232.105.3", "country": "RU" }, "target": { "port": 22, "service": "ssh" },
"indicators": [{ "type": "ip", "value": "91.232.105.3" }],
"status": "blocked", "count": 47, "last_seen": "2026-09-13T04:52:00Z" }
]
}`;
const KINDS: Array<[string, string]> = [
["finding", "Something in a public subject's code or configuration: a rule, a CWE, a location."],
["attack", "Traffic observed against the reporter's own infrastructure: source, target, count."],
["indicator", "A value worth blocking or watching on its own: ip, cidr, domain, url, hash, ua."],
["advisory", "A statement about a vulnerability, with the document in refs."]
];
const HARD: Array<[string, string]> = [
["A subject is public or it is not in the file", "A private repository, a customer's server, a paying user's scan: none of it is a threat in the open, it is someone's private security posture. A reporter that scans private things keeps two tables and serves one."],
["A secret is never located while it is open", "A secret finding is published with rule, severity, subject and status only. No location, no message, no excerpt. The credential is already exposed; the file must not be the map to it."]
];
const ABSENT: Array<[string, string]> = [
["No private subjects", "Stated in the rules and worth stating twice."],
["No exploit detail", "message says what was found; consequence what it means. How to use it is nobody's business here."],
["No scoring across reporters", "severity is the reporter's. A directory that normalises labels the result as its own."],
["No push", "A reporter serves a file. A directory watches updated."]
];
export default function OpenThreatPage(): ReactNode {
return (
<SiteShell active="OpenThreat">
<div className="band">
<div className="section-head">
<p className="eyebrow">LogicSRC standards surface</p>
<h2>OpenThreat</h2>
<p>
One file a security tool serves about what it found in the open. A directory reads the
reporter instead of a vendor feed, and the reporter decides what it discloses.
</p>
</div>
<p style={{ color: "#41505d" }}>
Every security tool finds things, and every one keeps what it found behind its own login.
A scanner that runs on a thousand public repositories knows which rules fire and where,
and says nothing, because saying it would mean a feed, a schema, an API key and a sales
call. The threat feeds that exist are products with terms that forbid redistribution.
OpenThreat is the small file a tool can serve in an afternoon at{" "}
<code style={mono}>/.well-known/openthreat.json</code>, with a rule for what may go in
it.
</p>
<p style={{ color: "#5b6b7a" }}>
Status: 0.1. The first reporter is{" "}
<a href="https://threatcrush.com/discovery">threatcrush.com/discovery</a>, built from the
scans its GitHub App ran on public repositories; the first directory is{" "}
<a href="https://nichedb.dev/c/threats">nichedb.dev/c/threats</a>.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>The descriptor</h2>
<p>
Only <code style={mono}>reporter.name</code> and a threat&apos;s{" "}
<code style={mono}>title</code> are required. Everything at{" "}
<code style={mono}>/.well-known/</code> is TLP:CLEAR by definition.
</p>
</div>
<pre style={pre}>{DESCRIPTOR}</pre>
<p style={{ color: "#41505d" }}>
<code style={mono}>rule</code> is the same string a SARIF ruleId carries;{" "}
<code style={mono}>subject</code> is what the threat is about and is public by
definition; <code style={mono}>status</code> is open, fixed, mitigated, blocked or
withdrawn, and a withdrawn threat stays in the file a while so directories retract it.
The second threat above is a secret: no location, no message, by rule.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>Four kinds</h2>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>kind</th>
<th style={th}>what it is</th>
</tr>
</thead>
<tbody>
{KINDS.map(([kind, what]) => (
<tr key={kind}>
<td style={td}>
<code style={mono}>{kind}</code>
</td>
<td style={td}>{what}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>Two rules that do not degrade</h2>
<p>Every other rule degrades. These two are the reason the file can exist at all.</p>
</div>
<table style={table}>
<tbody>
{HARD.map(([what, why]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{why}</td>
</tr>
))}
</tbody>
</table>
<p style={{ color: "#41505d" }}>
A subject that was scanned did not ask to be listed. Announcing is on by default, because
a finding in a public repository is public already, and opting out is one switch in the
tool&apos;s own settings. A subject that opts out leaves the file on the next build, and
is served once more as <code style={mono}>withdrawn</code> so directories retract it.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>What is deliberately absent</h2>
</div>
<table style={table}>
<tbody>
{ABSENT.map(([what, why]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{why}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>Where everything lives</h2>
</div>
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
<li>
<Link href="/docs/openthreat">Specification</Link>: the descriptor, twelve rules and the
two that do not degrade, announcing and opting out, discovery, SARIF, STIX and CSAF
</li>
<li>
<a href="https://threatcrush.com/discovery">threatcrush.com/discovery</a>: the first
reporter, and its policy page
</li>
<li>
<a href="https://nichedb.dev/c/threats">nichedb.dev/c/threats</a>: the first directory,
with RSS, JSON, API and MCP over the same rows
</li>
<li>
<Link href="/openprofile">OpenProfile.md</Link>, the operator behind a reporter;{" "}
<Link href="/openserver">OpenServer</Link> and <Link href="/opencoupon">OpenCoupon</Link>
, the same serve-your-own-file shape for other niches
</li>
</ul>
</div>
</SiteShell>
);
}

View file

@ -28,6 +28,7 @@ const STATIC_ROUTES: Array<{
{ path: "/openmcp", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openaccess", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openserver", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openthreat", changeFrequency: "weekly", priority: 0.9 },
{ path: "/opencpu", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openmemory", changeFrequency: "weekly", priority: 0.9 },
{ path: "/opengpu", changeFrequency: "weekly", priority: 0.9 },
@ -36,6 +37,7 @@ const STATIC_ROUTES: Array<{
{ path: "/opendisk", changeFrequency: "weekly", priority: 0.9 },
{ path: "/opencoupon", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openrecipe", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openaffiliate", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openontology/explore", changeFrequency: "daily", priority: 0.7 },
{ path: "/openspec", changeFrequency: "weekly", priority: 0.8 },
{ path: "/agent-swarm", changeFrequency: "weekly", priority: 0.8 },

View file

@ -20,6 +20,7 @@ const NAV: Array<{ href: string; label: string; external?: boolean }> = [
{ href: "/openmcp", label: "OpenMCP" },
{ href: "/openaccess", label: "OpenAccess" },
{ href: "/openserver", label: "OpenServer" },
{ href: "/openthreat", label: "OpenThreat" },
{ href: "/opencpu", label: "OpenCPU" },
{ href: "/openmemory", label: "OpenMemory" },
{ href: "/opengpu", label: "OpenGPU" },
@ -28,6 +29,7 @@ const NAV: Array<{ href: string; label: string; external?: boolean }> = [
{ href: "/opendisk", label: "OpenDisk" },
{ href: "/opencoupon", label: "OpenCoupon" },
{ href: "/openrecipe", label: "OpenRecipe.md" },
{ href: "/openaffiliate", label: "OpenAffiliate" },
{ href: "/#cli", label: "CLI" },
{ href: "/docs", label: "Docs" },
{ href: "/blog", label: "Blog" },

View file

@ -23,10 +23,12 @@ export const DOC_SLUGS = [
"openmcp",
"openaccess",
"openserver",
"openthreat",
"openfile",
"opendisk",
"opencoupon",
"openrecipe",
"openaffiliate",
"openstream",
"opencpu",
"openmemory",