import Link from "next/link"; import type { ReactNode } from "react"; import type { Metadata } from "next"; import { specMetadata } from "@/lib/page-meta"; import { SiteShell } from "@/components/site-shell"; import { mono, pre, table, td, th } from "../openontology/ui"; export const metadata: Metadata = specMetadata( "/openwebring", "OpenWebring is a webring that says who made it: one file a ring serves about its members at /.well-known/openwebring.json, one file a member may serve about itself, plain links between them, and a made_by declaration on every member, human, ai or both, so a reader can follow the human web or the machine web on purpose." ); const MEMBER = `{ "openwebring": "0.1", "site": { "url": "https://chovy.com/", "name": "Chovy's Blog", "feed": "https://chovy.com/feed.xml", "lang": "en", "author": "https://chovy.com/.well-known/openprofile.md" }, "made_by": "human", "disclosure": "ai-assisted", "ai_model": "gpt-5.1", "ai_provider": "OpenAI", "ai_prompt_url": "https://chovy.com/ai-methodology#posts", "rings": [ { "ring": "https://rssamplifier.com/ring/small-web", "slug": "chovy" } ] }`; const RING = `{ "openwebring": "0.1", "ring": { "slug": "small-web", "name": "Small Web", "url": "https://rssamplifier.com/ring/small-web", "accepts": ["human", "both"] }, "members": [ { "url": "https://chovy.com/", "slug": "chovy", "name": "Chovy's Blog", "feed": "https://chovy.com/feed.xml", "made_by": "human", "status": "active", "since": "2026-09-13", "checked": "2026-09-13T11:00:00Z" } ] }`; const LINKS = `← Small Web ring →`; const MADE_BY: Array<[string, string]> = [ ["human", "A person makes the content, with tools at most."], ["ai", "A model or agent makes it, with a person at most pointing it."], ["both", "A mix the site does not care to split."], ["absent", "Unstated. A directory shows it as unstated, never as human."] ]; const HOPS: Array<[string, string]> = [ ["A 302 and nothing else", "No cookie, no interstitial, no counter. Cache-Control: no-store. Location is the member's url."], ["from, and every older shape", "?from= is canonical; ?host=, ?via=, ?url=, a slug in the path and the bare Referer all resolve, so a member of any existing ring joins with no change."], ["Unknown from is random", "A member that moved or a reader with no referrer still lands in the ring."], ["The ring wraps", "After the last member comes the first."], ["Inactive is skipped, not removed", "A member whose link went missing stays in the list, shown and skipped, until it is back."], ["random never returns you", "When the ring has more than one member."] ]; const ABSENT: Array<[string, string]> = [ ["No script", "A member is a link. A widget is a host's offer, never a requirement."], ["No verification of made_by", "It is a statement. Every badge scheme before this one said the same and was right to."], ["No central registry", "A ring is a file on a host; a directory is optional. Two rings with one name on two hosts are two rings."], ["No reader tracking", "A hop is a redirect, cached by nobody, remembered by nobody."], ["No invented relation for the hops", "next and prev are registered; me is identity; openwebring on a link points at a descriptor and nothing else."] ]; export default function OpenWebringPage(): ReactNode { return (

LogicSRC standards surface

OpenWebring

A webring that says who made it. One file a ring serves about its members, one a member may serve about itself, plain links between them, and a declaration on every member: human, ai, or both.

Webrings came back, hundreds of them, and every one reinvents the same three things: a member list kept by hand in its own shape, a next and a previous link, and a check by eye that the member still links back. Meanwhile the one thing a reader in 2026 wants to know about a site, whether a person wrote it, has nowhere to be said that a ring can filter on. A ring for the human web and a ring for the machine web are the same ring with one field.

Status: 0.1.1. The first host is{" "} rssamplifier.com, one ring per topic out of the feeds it already reads; the first directory reading rings is{" "} nichedb.dev/c/webrings. A member owes a ring one plain link, which is what every ring already asks for.

The member

The whole obligation is three anchors. The descriptor is optional and says the rest.

{LINKS}
{MEMBER}

rings is the one thing no existing format carries: the site's own statement of which rings it belongs to. disclosure{" "} is optional and uses the W3C AI Content Disclosure vocabulary verbatim, with{" "} ai_model, ai_provider and{" "} ai_prompt_url beside it, so all four map onto the group's HTML attributes and the IETF header without translation.

made_by

A self-declaration. Nothing verifies it, and a directory says so beside it.

{MADE_BY.map(([value, means]) => ( ))}
value means
{value} {means}

The ring

Listed at /.well-known/openwebring.json on the host, members in ring order at members_url, and the same members as OPML.

{RING}

accepts is the ring's policy on made_by. A host checks members on a schedule, the way the IndieWeb ring does: it fetches the page, looks for any link to the ring, and marks active or{" "} inactive. It never removes a member for a missing link.

Six hop rules

{HOPS.map(([what, how]) => ( ))}
{what} {how}

What is deliberately absent

{ABSENT.map(([what, why]) => ( ))}
{what} {why}

Where everything lives

  • Specification: the member, the host, the two files, six hop rules, verification, discovery, what a directory owes a ring
  • rssamplifier.com/ring: the first host, one ring per topic and a ring of the sites Profullstack publishes
  • nichedb.dev/c/webrings: the first directory reading rings, every ring and every member across hosts, with who makes each site
  • OpenProfile.md, the author behind a member;{" "} OpenCoupon and OpenServer, the same serve-your-own-file idea for other things a site knows about itself
); }