docs: OpenCoupon and OpenRecipe.md, the first two niche specs

Anthony: every top-level nichedb.dev niche may need its own open<niche>
spec so the serve-your-own-file pattern scales across industries. These
are the two he named first.

OpenCoupon: one JSON file a merchant serves at
/.well-known/opencoupon.json about what is on offer right now: every
code, sale and shipping threshold with kind (percent, amount, shipping,
bogo, gift, other), value, scope, min_order, dates, status, per-customer
and region limits. Expired coupons stay in the file so a directory
learns a code died from the one party that knows. No affiliate links,
no redemption, no votes. First reader: nichedb.dev/c/deals.

OpenRecipe.md: one Markdown file that is a recipe, in the OpenProfile.md
and OpenResume.md style: a summary block (Serves, Prep, Cook, Cuisine,
Course, Diet, Author, Source, Image), a description line, Ingredients
and Steps as written, Notes, Nutrition per serving. Served next to the
page, linked with rel="openrecipe", or indexed at
/.well-known/openrecipe.md. A one-way mapping to schema.org/Recipe:
the JSON-LD is generated from the Markdown, never the reverse.

Both registered in DOC_SLUGS, NAV, STATIC_ROUTES and llms.txt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014cmNRtR2vL1p89dbVQ7FZJ
This commit is contained in:
Anthony Ettinger 2026-09-13 02:04:39 +00:00
parent 41c362ddd8
commit c3f051736b
8 changed files with 742 additions and 0 deletions

View file

@ -24,6 +24,8 @@ export function GET(): Response {
- [OpenMCP](${SITE_URL}/openmcp): An open catalog of MCP relays: a relay serves /.well-known/openmcp.json, a catalog probes it and lists only what it found, and clients reach every relay through the catalog's REST, its own MCP endpoint, or signed webhooks.
- [OpenAccess](${SITE_URL}/openaccess): OAuth 2.1 with a grant you can carry: one hub account per person or agent, apps keep their own users and link them once, grants delegate narrower to agents, and a subscription bought in one app is honoured by every app that honours the product. Reference hub at openaccess.logicsrc.com.
- [OpenServer](${SITE_URL}/openserver): One file a hosting provider serves about what it sells, at /.well-known/openserver.json: every offer with kind (cloud, vps, dedicated, bare-metal, colocation, on-prem, shared, managed, paas, serverless, storage, gpu, edge, p2p, hybrid), the premises, management, tenancy and model axes, specs, one price, location and stock. Directories read the provider instead of scraping; first reader is nichedb.dev/c/hosting.
- [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.
- [AgentSwarm](${SITE_URL}/agent-swarm): Provider-neutral agent orchestration, model routing, and cost controls.
- [AgentByte](${SITE_URL}/agentbyte): Agent screening sessions, policy events, and APIs.
- [Credential Sharing](${SITE_URL}/credential-sharing): End-to-end-encrypted team vaults, plus source/target credential diffs, approval, sync, rollback, and audit.

View file

@ -0,0 +1,185 @@
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: "OpenCoupon · LogicSRC",
description:
"OpenCoupon is one file a merchant serves about what is on offer right now, at /.well-known/opencoupon.json: every code, sale and shipping threshold with its 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.",
alternates: { canonical: "/opencoupon" }
};
const DESCRIPTOR = `{
"merchant": { "name": "Northwind Outfitters", "web": "https://northwind.example",
"currency": "USD", "terms": "https://northwind.example/promotions/terms" },
"updated": "2026-09-13T06:00:00Z",
"coupons": [
{ "id": "fall15", "code": "FALL15", "title": "15% off everything for fall",
"kind": "percent", "value": 15, "min_order": 50,
"scope": { "excludes": ["gift-cards", "sale"] },
"ends": "2026-09-30T23:59:59Z", "new_customers": false, "stackable": false,
"regions": ["US", "CA"], "status": "active" },
{ "id": "ship-75", "title": "Free shipping over 75", "kind": "shipping", "min_order": 75 },
{ "id": "boots-sale", "title": "Trail boots, 40 off", "kind": "amount", "value": 40,
"url": "https://northwind.example/boots/trail", "price": { "was": 160, "now": 120 } },
{ "id": "summer10", "code": "SUMMER10", "title": "10% off summer", "kind": "percent",
"value": 10, "ends": "2026-08-31T23:59:59Z", "status": "expired" }
]
}`;
const KINDS: Array<[string, string, string]> = [
["percent", "value is a percentage", "15% off, capped by max_discount when stated"],
["amount", "value is an amount in currency", "40 off; price {was, now} shows the cut on a sale"],
["shipping", "no value", "free shipping at min_order"],
["bogo", "value bought, gets given", "buy 2 get 1"],
["gift", "gift names the item", "a gift with purchase"],
["other", "the merchant's own words", "kept, listed, not filtered on"]
];
const DIRECTORY: Array<[string, string]> = [
["Hourly, at least", "Coupons start and end on the hour. A file read once is a snapshot."],
["Dedupe on origin + id", "A re-read updates the row. An id that leaves the file is marked gone, not deleted."],
["Expiry with the read time", "A code shown as active is active as of a stated moment."],
["The merchant's url, unchanged", "A directory that routes through its own tracking says so beside the link."],
["Verified above claimed", "A code from the merchant's origin outranks the same code from a forum, and the page shows which is which."]
];
const ABSENT: Array<[string, string]> = [
["No affiliate links", "url is the merchant's. A commission link is the directory's own, marked as such, beside it."],
["No redemption", "The file says a code exists and what it does. Whether checkout accepts it for this cart is checkout's business."],
["No votes, no badges", "The origin is the verification. A score a directory adds is labelled as its own."],
["No catalog prices", "A coupon names what it applies to; the store says what that costs. price on a sale is the one exception."]
];
export default function OpenCouponPage(): ReactNode {
return (
<SiteShell active="OpenCoupon">
<div className="band">
<div className="section-head">
<p className="eyebrow">LogicSRC standards surface</p>
<h2>OpenCoupon</h2>
<p>
One file a merchant serves about what is on offer right now. A coupon site reads the
merchant instead of a forum thread, and a dead code dies everywhere at once.
</p>
</div>
<p style={{ color: "#41505d" }}>
Every coupon site is a graveyard. A code is posted once, copied everywhere, and lives on
for years after the merchant retired it, because no coupon site knows when a code died
and the merchant has no way to tell them. The merchant already knows exactly which codes
work: its checkout is the source of truth and the promotion lives in a table with a start,
an end and a rule. OpenCoupon is that table, exported, at{" "}
<code style={mono}>/.well-known/opencoupon.json</code>.
</p>
<p style={{ color: "#5b6b7a" }}>
Status: 0.1. The first directory reading it is the deals collection at{" "}
<a href="https://nichedb.dev/c/deals">nichedb.dev</a>. The smallest valid file is a
merchant with a name and a coupon with a title.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>The descriptor</h2>
<p>
A code fetched from the merchant&apos;s own origin is a code the merchant says works.
That is the whole verification, and the whole point.
</p>
</div>
<pre style={pre}>{DESCRIPTOR}</pre>
<p style={{ color: "#41505d" }}>
<code style={mono}>code</code> absent means nothing to type: the promotion applies on its
own at <code style={mono}>url</code>. <code style={mono}>scope</code> narrows to
categories or products, or everything but <code style={mono}>excludes</code>.{" "}
<code style={mono}>status</code> is derived from the dates unless stated, and an expired
coupon stays in the file for as long as copies of it circulate, so a directory learns it
died from the one party that knows. Unknown keys are kept.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>Six kinds</h2>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>kind</th>
<th style={th}>value</th>
<th style={th}>example</th>
</tr>
</thead>
<tbody>
{KINDS.map(([kind, value, example]) => (
<tr key={kind}>
<td style={td}>
<code style={mono}>{kind}</code>
</td>
<td style={td}>{value}</td>
<td style={td}>{example}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>What a directory owes a merchant</h2>
</div>
<table style={table}>
<tbody>
{DIRECTORY.map(([what, how]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{how}</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/opencoupon">Specification</Link>: the descriptor, ten rules, six
kinds, expired coupons, discovery, what a directory owes a merchant
</li>
<li>
<a href="https://nichedb.dev/c/deals">nichedb.dev/c/deals</a>: the first directory
reading it, beside the deal communities it reads today
</li>
<li>
<Link href="/openserver">OpenServer</Link>, the same idea for a hosting provider&apos;s
catalog; <Link href="/openprofile">OpenProfile.md</Link>, the operator behind a merchant
</li>
</ul>
</div>
</SiteShell>
);
}

View file

@ -0,0 +1,211 @@
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: "OpenRecipe.md · LogicSRC",
description:
"OpenRecipe.md is one Markdown file that is a recipe: a summary block (Serves, Prep, Cook, Cuisine, Diet, Author, Source), ingredients and steps as written, notes and nutrition, served next to the recipe page or linked with rel=\"openrecipe\". schema.org/Recipe JSON-LD is derived from it, never the reverse.",
alternates: { canonical: "/openrecipe" }
};
const EXAMPLE = `# Shakshuka
- **Serves**: 4
- **Prep**: 10 min
- **Cook**: 25 min
- **Cuisine**: North African
- **Diet**: vegetarian, gluten-free
- **Author**: Ada Lovelace
- **Source**: https://ada.example/recipes/shakshuka
Eggs poached in a spiced tomato and pepper sauce. One pan, bread on the side.
## Ingredients
- 2 tbsp olive oil
- 1 onion, diced
- 800 g canned crushed tomatoes
- 6 eggs
- parsley, chopped (to serve)
## Steps
1. Heat the oil in a wide pan. Cook the onion until soft, about 8 minutes.
2. Pour in the tomatoes, season, and simmer 10 minutes until thickened.
3. Make six wells, crack an egg into each, cover, cook 5 to 8 minutes.
## Nutrition
- **Calories**: 260
- **Protein**: 14 g`;
const RULES: Array<[string, string]> = [
["One # heading", "The name of the dish. More than one and the first wins."],
["The summary block", "Serves or Yield, Prep, Cook, Total, Cuisine, Course, Diet, Author, Source, Image are understood; unknown keys are kept."],
["The description", "One prose line between the block and the first ##."],
["## opens a section", "ingredients, steps (method, directions, instructions), notes, nutrition, equipment, variations. Unknown sections are kept."],
["Ingredients as written", "One bullet per ingredient: 1 onion, diced. A reader parses a leading quantity when it can and keeps the line whole when it cannot. ### groups them."],
["Steps in order", "One numbered item per step. A time in the text may become a timer but stays in the text."],
["Nutrition per serving", "Key: value with the unit written; Per changes the basis. Never computed silently."],
["Source and Author", "An adapted recipe names where it came from and says what changed in Notes."]
];
const MAPPING: Array<[string, string]> = [
["# heading", "name"],
["Serves / Yield", "recipeYield"],
["Prep, Cook, Total", "prepTime, cookTime, totalTime as ISO 8601 durations"],
["Cuisine, Course, Diet", "recipeCuisine, recipeCategory, suitableForDiet"],
["Ingredients bullets", "recipeIngredient, one string each, as written"],
["Steps items", "recipeInstructions as HowToStep, ### groups as HowToSection"],
["Nutrition", "nutrition as NutritionInformation"]
];
const ABSENT: Array<[string, string]> = [
["No required fields beyond the name", "A name and a list of ingredients is a valid recipe. So is a name and a list of steps."],
["No unit system", "Grams and cups are both kept as written. A reader converts on display and says it did."],
["No ingredient grammar", "1 onion, diced is one line, and every cooking app already parses lines like it."],
["No ratings, no story", "The page has those. The file is the recipe."],
["No JSON", "The structured view is derived and regenerated from the Markdown on every read."]
];
export default function OpenRecipePage(): ReactNode {
return (
<SiteShell active="OpenRecipe.md">
<div className="band">
<div className="section-head">
<p className="eyebrow">LogicSRC standards surface</p>
<h2>OpenRecipe.md</h2>
<p>
One Markdown file that is a recipe, in the form a person writes one and a printer prints
one. The page has the story. The file has the recipe.
</p>
</div>
<p style={{ color: "#41505d" }}>
A recipe on the web is four thousand words of memoir with the recipe at the bottom, and a
block of JSON-LD in the head that search engines read and people never see. The two
drift: the page says three eggs and the schema says two. The form people actually write
recipes in has not changed in a century and it is Markdown already. OpenRecipe.md fixes
where the file lives and which lines mean what, so a cooking app, a grocery list and an
agent read the file the author wrote.
</p>
<p style={{ color: "#5b6b7a" }}>
Status: 0.1. Same rules as <Link href="/openprofile">OpenProfile.md</Link> and{" "}
<Link href="/docs/openresume">OpenResume.md</Link>: Markdown is canonical, every rule
degrades, the structured view is derived.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>The shape</h2>
</div>
<pre style={pre}>{EXAMPLE}</pre>
</div>
<div className="band">
<div className="section-head">
<h2>The eight rules</h2>
<p>Every one of them degrades rather than fails.</p>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>Rule</th>
<th style={th}>What it means</th>
</tr>
</thead>
<tbody>
{RULES.map(([rule, meaning], index) => (
<tr key={rule}>
<td style={td}>
<strong>
{index + 1}. {rule}
</strong>
</td>
<td style={td}>{meaning}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>Discovery</h2>
<p>
Next to the page (<code style={mono}>/recipes/shakshuka.md</code>), a{" "}
<code style={mono}>{'<link rel="openrecipe">'}</code> on the page, or a site index at{" "}
<code style={mono}>/.well-known/openrecipe.md</code>, one recipe per bullet. Served as{" "}
<code style={mono}>text/markdown</code>.
</p>
</div>
</div>
<div className="band">
<div className="section-head">
<h2>schema.org, one way</h2>
<p>
Keep serving schema.org/Recipe for search engines. Generate it from the Markdown on
every publish. The Markdown is the canonical copy.
</p>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>OpenRecipe.md</th>
<th style={th}>schema.org/Recipe</th>
</tr>
</thead>
<tbody>
{MAPPING.map(([md, schema]) => (
<tr key={md}>
<td style={td}>{md}</td>
<td style={td}>
<code style={mono}>{schema}</code>
</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/openrecipe">Specification</Link>: the shape, eight rules, discovery,
the schema.org mapping
</li>
<li>
<Link href="/openprofile">OpenProfile.md</Link>, the Author behind a recipe;{" "}
<Link href="/opencoupon">OpenCoupon</Link>, the same serve-your-own-file idea for a
merchant&apos;s promotions
</li>
</ul>
</div>
</SiteShell>
);
}

View file

@ -26,6 +26,8 @@ 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: "/opencoupon", changeFrequency: "weekly", priority: 0.9 },
{ path: "/openrecipe", 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

@ -18,6 +18,8 @@ const NAV: Array<{ href: string; label: string; external?: boolean }> = [
{ href: "/openmcp", label: "OpenMCP" },
{ href: "/openaccess", label: "OpenAccess" },
{ href: "/openserver", label: "OpenServer" },
{ href: "/opencoupon", label: "OpenCoupon" },
{ href: "/openrecipe", label: "OpenRecipe.md" },
{ href: "/#cli", label: "CLI" },
{ href: "/docs", label: "Docs" },
{ href: "/blog", label: "Blog" },

View file

@ -21,6 +21,8 @@ export const DOC_SLUGS = [
"openmcp",
"openaccess",
"openserver",
"opencoupon",
"openrecipe",
"openstream",
"openspec-comparison",
"data-model",