OpenListing 0.1: one file a seller serves about a thing on offer (#213)

A house for sale, an apartment to rent, a car. One document per listing,
on the seller's own origin, so a directory reads the seller's file
instead of licensing somebody else's database or scraping a marketplace
that forbids it.

**The name `OpenRental` was the obvious one and it is taken.** It
already means renting OpenAgent and OpenSwarm members through CoinPay,
with a published schema, SDK, validators and fixtures. Its own document
records surviving one collision already: it was OpenFleet until
2026-09-13. Reusing the name for housing would have broken a shipped
0.1 spec, so the family is OpenListing instead.

Two axes rather than a spec per combination. A house for sale and a
house to rent are the same house; only the offer differs. A house for
sale and a car for sale are the same offer; only the subject differs.
Writing OpenHouseForSale, OpenHouseForRent, OpenCarForSale and the rest
produces one spec per cell of a grid, each repeating most of the others.
So `offer.type` carries sale/rent/lease/auction/free/wanted and
`subject.type` carries property/vehicle, and a subject profile adds only
the fields peculiar to its subject. A reader that knows OpenListing but
not a profile still reads price, location, offer and dates correctly.

`openhouse` is not used as a name: in real estate an open house is a
viewing event, not a kind of listing. An actual one is an entry in
`showings`.

The problem section is grounded rather than asserted: RESO tracks 484
separate MLSs as of August 2026, down from nearly twice that in 2015;
each does publish a RESO Web API feed, and each requires a real estate
licence, a signed per-MLS data agreement and vendor credentials. Four
hundred and eighty-four negotiations for one national view, not
redistributable. Vehicle parts are the same shape with different
letters (ACES/VCdb, TecDoc).

Decisions worth naming. Prices are decimal STRINGS, because 2450.10 is
not representable in binary floating point and a listing is a price.
Absent means unstated, never zero -- with `bedrooms: 0` called out in
the property profile as the one place where zero genuinely differs and a
studio is not an unstated bedroom count. `location.precision` lets a
seller publish a postal code rather than a street for an occupied home,
and forbids a reader from drawing a pin on a building anyway. A closed
listing SHOULD stay served for 90 days with `closed_at`, because a
directory that learns from a 404 cannot tell "sold" from "outage" --
the failure that makes every coupon site a graveyard.

The vehicle profile treats a VIN as a claim rather than a proof, points
readers at NHTSA vPIC (free, keyless, public domain) for decoding rather
than making sellers restate what the VIN encodes, and puts parts fitment
explicitly out of scope: that is ACES/VCdb or TecDoc, and a public spec
cannot restate licensed data.

Registered as one parent and two blocks under it, using the registry's
existing `parent` field the way OpenServer's blocks already do. Flat
slugs rather than nested paths because /docs/[slug] is a single dynamic
segment, not a catch-all -- a nested slug would not have routed at all.

nichedb.dev is named as the reference directory.

50 contract tests pass including spec-discovery and page-metadata.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-09-25 03:14:19 -07:00 • committed by GitHub
parent 699eff427a
commit 32bbdeda95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 359 additions and 0 deletions

View file

@ -95,6 +95,9 @@ export const FAMILIES: Family[] = [
s("opensaas", "OpenSaaS", "One file a subscription service serves about the way in and the way out of every plan, for a person and for an agent"),
s("openmodel", "OpenModel", "One file a model provider serves about the models it serves and what they cost: price per million tokens, context, modalities and what each model can do", { landing: undefined }),
s("openaffiliate", "OpenAffiliate", "One file a merchant serves about the commission it pays"),
s("openlisting", "OpenListing", "One file a seller serves about one thing it is offering: what it is, what it costs, on what terms and where, with the offer and the subject as separate axes so a house to rent and a car for sale are one format", { landing: undefined }),
s("openproperty", "OpenProperty", "The property subject: houses, apartments, rooms and land, with tenure, area and the floor-versus-floors distinction that property data usually gets wrong", { parent: "openlisting", landing: undefined }),
s("openvehicle", "OpenVehicle", "The vehicle subject: cars, motorcycles, trucks and boats, with the VIN as a claim rather than a proof and odometer units always stated", { parent: "openlisting", landing: undefined }),
s("openrecipe", "OpenRecipe.md", "One Markdown file that is a recipe, with schema.org derived from it and never the reverse"),
s("opensong", "OpenSong", "One plain-text file that is a song: title, style, exclusions and lyrics as the blocks a generator takes, kept beside the audio"),
s("openemoji", "OpenEmoji", "An emoji set as a folder: one file that states coverage, licence and whether a person or a model drew it, and glyphs named by the codepoints they draw"),