`railway up` writes .nixpacks/ into the working tree: a Dockerfile, a
nixpkgs pin, and a build.sh that hard-codes the absolute path of the
worktree it ran in plus a one-off image tag. None of it is reusable and
no workflow reads it, so every branch that deploys regenerates it and
conflicts with the last one -- which is exactly how this branch ended up
CONFLICTING against master.
Drop it and ignore it.
Railway deployed this on push; dev2 does not by itself. The workflow ssh's
to the box and runs /home/anthony/www/logicsrc.com/deploy-app.sh, which builds
the image from this checkout and restarts the compose stack. Generated by
cli-tools dev2/dev2-site scaffold.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Railway deployed this on push; dev2 does not by itself. The workflow ssh's
to the box and runs /home/anthony/www/app.logicsrc.com/deploy-app.sh, which builds
the image from this checkout and restarts the compose stack. Generated by
cli-tools dev2/dev2-site scaffold.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Anthony asked for OpenCar twice after I argued for OpenVehicle on the
grounds that it covered boats. His name is the better one: OpenCar is
what a person reaches for, and a site that lists cars lists the
motorcycle and the pickup beside them.
So rather than keep a name that disagrees with its contents, the scope
moves to match the name. `kind` is now car, motorcycle, truck, van,
suv, bus, rv, atv, other -- road vehicles. Boats and heavy equipment are
dropped from it and belong in sibling profiles, which cost nothing to
add because the parent does not change. Keeping them here would have
meant every field being optional and meaningless for half the subjects.
`subject.type` is `car` and the key is `subject.car`, so the axis value
and the profile agree.
Renamed the day it shipped and before anything reads it: no descriptor
exists yet, and nichedb's directory has not been built. Doing it later
would have meant a migration.
50 contract tests pass, spec-discovery included.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
There is no shortage of places to look up an AI model and nowhere to look
up what it costs you, from the provider that will bill you, in a form a
program can read. Every provider publishes prices as a marketing page and
every comparison site scrapes those pages. The best of them, models.dev,
is a community database: 8,179 provider offerings across 223 providers,
maintained by volunteers reading pricing pages. Good work, and still a
third party writing down what a company charges.
OpenModel is that table served by its author at
/.well-known/openmodel.json: price per million tokens in, out and cached,
context and output limits, modalities, and what each model can do.
The unit is the offering, a provider and a model together, because the
same model reaches a buyer from many providers at many prices and the
price is the question they came with.
Two rules decide what a row means, and both come from measuring the real
data. A published zero is a fact: of those 8,179 offerings, 638 cost
nothing and 424 published no price at all, so a reader that collapses the
two invents a free model or hides one. And absent is unstated, never
false, because a false travels further than a blank and comes back quoted
as a fact.
Origin is the proof, as with every catalog spec here: a file naming a
provider's web and served by somebody else is a stranger's claim about
that company's prices.
The first reader is the models collection at nichedb.dev/c/models, which
reads descriptors beside the models.dev catalogue in the same vocabulary
so one feed catches both.
Registered in the one registry, with docs/openmodel.md and a docs-only
entry (no landing page yet), and added to the spec-discovery contract so
the family page, docs index, llms feeds and sitemap are proven to carry
it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anthony, on the gallery with the Brands filter and Agentic Emissive
selected: "why doesn't brands change?" It could not. Every brand file
was byte-identical across all four colour styles — github.webp was the
same 606-byte file in hq, matte, machined and emissive — because a
colour style stopped at the drawn icons and a brand's coloured form was
just the line mark recoloured to the owner's hex.
The three agentic styles now re-render the marks in their material,
pinned to the owner's own mark: same geometry, same colour, new surface.
hq keeps the flat marks a first-release reader already has.
Spec:
- Rule 7 said "a set does not redraw them", which forbade exactly this.
It now says what a re-rendering may and may not do: geometry, counters
and figure/ground are fixed, only the surface changes.
- Rule 8 gains the third provenance: a re-rendered mark is `both`, the
geometry its owner's and the material the set's.
- Rule 10 covers every colour style rather than hq alone, and says what
a material that works by emitting light does with a mark too dark to
emit: its own colour where it can, a lighter tint of the same hue
where it is dark but still coloured, neutral white where there is no
hue left. 85, 4 and 22 of the 111.
- Rule 12 says how a reader tells the two apart without comparing files:
a flat style writes `svg` and `made_by: human`, a re-rendering style
writes no `svg` and `made_by: both`.
- Rule 13 gains `ground`. It already observed that a near-black style is
right on a terminal and illegible on paper, but gave a reader no way
to know which style that was.
Gallery:
- Honours `ground: dark`, so emissive draws on the dark tiles it needs.
Without it the 22 marks that emit neutral white — GitHub, Apple,
OpenAI — were invisible on a white tile, which is the same class of
bug as the one that started this.
- In the detail hero each style tile carries its own ground, so a
dark-only style sitting beside three light ones still shows something.
The hosted set carries the new brand webp for the three agentic styles
and drops the 333 flat marks they no longer reference.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The detail panel's hero was a flex row that could not wrap, and the style
tabs were a segmented group that could not shrink. Both were written when
a set had one colour style; with hq plus the three agentic styles the
later tiles fell outside the panel entirely ("Agentic Machined" and
"Agentic Emissive" rendered past the right edge) and the tab group's
min-content width dragged the whole page onto a horizontal scrollbar on
a phone.
- hero: style previews move into their own auto-fit grid that reflows,
72px -> 64px so four fit a 24rem panel, labels wrap and centre. The
margin-left:auto that positioned the single old tile is gone.
- toolbar: the grid column is minmax(0, 1fr) rather than auto, so no one
unbreakable control can widen the page, and the style group scrolls
inside its own border.
Both are sized by what the set ships, so a fifth style costs nothing.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* OpenIcon: a set may carry several colour styles
Rules 12 and 13. HQ was the first colour style and it was drawn beside
glossy 3D emoji masters, which reads as the decade it borrows from, so
a colour style cannot be one look forever: the reference set now carries
four, HQ plus three flat-material agentic styles.
Rule 12 makes more than one a map rather than a second name: `styles`
lists them, `style_info` describes each, and each icon carries a `styles`
map whose entries are shaped exactly as rule 9's `hq` block. A set with
an HQ style keeps writing the old top-level `hq` block and per-icon `hq`
key as the same objects under their older name, so a reader written
against the first HQ release keeps working.
Rule 13 makes a style say what it is for. The choice between colour
styles is a choice of surface: near-black bodies with lit accents are
right on a dark terminal and illegible on paper, and only the set knows
which is which. A family shares a prefix and the family name alone means
its default member, so `agentic` means `agentic-matte`.
The gallery follows the manifest instead of a simple/HQ switch: one tab
per style the set has actually drawn, labelled and explained from
`style_info`, and the detail pane shows every style an icon has.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* openicon gallery: serve the three agentic styles
The hosted copy of the reference set gets the new styles' 64 and 128px
webp and the brand SVGs, plus the manifest that now lists all four
colour styles, so the gallery's tabs have something behind them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.3.0 has been the reported version through three merges that changed what the
vault can import: Bitwarden JSON read natively, then ids, timestamps, password
history and URI match rules preserved, then 1Password .1pux plus six more CSV
products and a --source that names a product rather than a file format.
install.sh reads the version straight out of packages/cli/package.json, so
`logicsrc --version` and install.json both said 0.3.0 on a build that already
carried all of it. Anyone checking a version to tell whether their CLI could read
a 1Password export got the wrong answer.
Only the CLI moves. @logicsrc/opencreds stays at 0.1.0 deliberately: its version
is hardcoded in three places and one of them is the GENERATOR stamp written into
every exported database, so bumping it changes a file-format field. That is worth
doing on its own terms, not as a side effect of making --version honest.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two gaps. Format was decided inline in the import command by whether the text
started with a brace, and --source only ever reached the CSV reader -- so a JSON
or archive export could not be forced at all. If the sniff was wrong there was no
way to say "this came from 1Password". And 1Password's own export button produces
a .1pux, which nothing here could open.
A source now names a product rather than a file format. Bitwarden exports JSON
and CSV; 1Password exports .1pux and CSV. Saying --source onepassword says where
the file came from, and the container is still decided by looking at the bytes.
One router owns that decision instead of the command, and when nothing can read a
file it prints every source that can be named rather than the bare "pass
--source" it used to.
1Password's .1pux is a ZIP holding a single JSON document. Pulling in a zip
library for that would have been this package's only dependency beyond commander,
so the central directory is read here: node's zlib already does the
decompression, and the container is a few dozen lines of offsets. Deliberately
not a general ZIP implementation -- no encryption, no ZIP64, only the two
compression methods an export uses.
The .1pux reader keeps vaults as folders, TOTP secrets, custom sections, password
history and the whole of a card. 1Password item ids are 26-character base32
rather than UUIDs, so they are hashed into a v5-shaped UUID: the same export
imported twice produces the same ids, which is what makes a re-import report its
items as already present instead of duplicating the vault. Trashed items are left
behind and reported, since restoring deleted entries into a fresh vault would be
a surprise. A category we do not model -- a passport, a server, a licence --
becomes a note carrying its fields, so an import never quietly loses one.
Six more CSV products join the existing five: NordPass, Dashlane, Proton Pass,
RoboForm, Apple Passwords and Firefox. Adding Apple broke 1Password: their
columns are nearly identical and only 1Password's `type` separates them, so Apple
now requires its absence and 1Password is asked first. There is a test for that
pair, because the failure mode is silent -- every 1Password CSV had started
importing as Apple.
LastPass and KeePass needed nothing: LastPass only ever exports CSV, which was
already read, and the same is true of KeePass's CSV.
Verified end to end: the real 4,395-item Bitwarden export still reads, a .1pux
round-trips through the CLI, an unidentifiable CSV prints the source list and
then imports once told, and an unknown source name is refused by name. The zip
reader is tested against archives the system zip produced rather than ones we
wrote. 195 tests pass.
The 1Password mapping is built from the documented 1PUX schema and driven by
fixtures, not from a real 1Password export. Run it with --dry-run first.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four things the first pass threw away, all of them recorded in the mapping notes
from the 2026-09-02 hand conversion and all of them silent.
Item ids were regenerated. Bitwarden ids are already UUIDs, so keeping them is
what makes a re-import idempotent: run the same export twice and the second run
reports its items as already present under "skip", rather than duplicating the
entire vault. createItem deliberately refuses a caller-supplied id and always
mints a fresh one, so the id is adopted after construction, and only when it
really is a UUID.
Timestamps were restamped to "now". creationDate and revisionDate are the only
record of when a password was last rotated, and overwriting them destroys that
permanently. The oldest item in a real export dates to 2018; every one of them
would have been dated today.
Password history was dropped entirely. It is carried now, newest first, mapping
lastUsedDate to changedAt and capped at MAX_HISTORY_ENTRIES. 189 items in a real
export have history, so this was not a rare case.
URI match rules were hardcoded to "domain". Bitwarden stores them as a number --
0 domain, 1 host, 2 startsWith, 3 exact, 4 regex, 5 never, with null meaning
domain -- and flattening them quietly widens a login pinned to an exact URL,
which is a security change rather than a cosmetic one.
Verified on the same 4,395-item export: all 4,395 ids carried across, stable
across two runs, 189 items with history recovered, and the oldest createdAt still
2018-02-22. That export happens to use domain matching throughout, so the match
mapping is covered by tests rather than by it.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Importing a Bitwarden export failed with "Not an OpenCreds database" and had to
be converted by hand first. The import command decided what a file was by
`text.trimStart().startsWith("{")`, and treated every JSON as an OpenCreds
database. A Bitwarden export starts with a brace too, so it went to parseDatabase
and was rejected there.
JSON is the format Bitwarden's own UI hands you by default, and the only one of
its formats that keeps folders, custom fields, multiple URIs per login and full
card/identity detail. Its CSV drops all of that, so "export as CSV instead" is a
lossy workaround rather than an answer.
The shape is now sniffed before deciding which reader owns the file: an `items`
array plus either a `folders` array or the `encrypted` flag. An OpenCreds
database has neither at its top level, so the two never collide, and a cheap
substring test means a large database is not parsed twice to find that out.
Everything the format carries is mapped: all four item types, Bitwarden's own
folder ids (so two folders sharing a name stay distinct), custom fields with
their hidden flag, every URI rather than only the first, TOTP secrets, and
favourites. An untitled login is still named after its host. A folderId naming no
folder is dropped rather than inventing a folder, and only folders something
actually landed in come back, so importing one item out of a big export does not
create sixteen empty folders beside it.
An encrypted export is refused outright instead of half-read. Its items are
opaque strings, so a best-effort parse would store ciphertext as if it were a
password and leave a vault full of junk that never decrypts.
Verified against a real 4,395-item export: 4,387 logins, 3 cards, 4 identities,
1 note, 16 folders, zero skipped, parsed in 56ms. Every count matches the source
file, and the command that used to fail now reports "(Bitwarden JSON)".
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
All 3,963 emoji (previews 64/128 and openemoji.json with CLDR keywords
and oe_ shortcodes), and platforms.json: 23 networks, 324 packs, steps
and limits, downloads from the openemoji release. The catalog's Install
on picker appears with it.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reddit, Slack, X and Facebook read og:title, and every page of
logicsrc.com was serving the homepage's. The <title> tag was correct per
page, which is why it went unnoticed: Next.js shallow-merges metadata, so
a page exporting only `{ title, description, alternates }` inherits the
root layout's `openGraph` object whole. og:title, og:description and
og:url were the homepage's on all 45 spec landing pages, every doc, every
report and every SPA section.
Blog posts were the one route that declared openGraph, and they proved
the mechanism: their og:title is right. They also showed the other half
of it -- declaring openGraph drops the file-convention opengraph-image,
so live posts shipped with no og:image at all, and their twitter:title
was still the homepage's.
- src/lib/page-meta.ts: one builder, three entry points. specMetadata
titles a spec landing page from its lib/specs.ts entry, pageMetadata
titles the site's own pages from PAGE_META, contentMetadata titles a
doc, post, skill or ontology record from its content. All three always
emit openGraph and twitter title, description, url and image.
- Spec titles come from the registry, so they cannot drift from the way
the sidebar, /specs and the home page describe the same spec, and a new
spec page is titled the moment its registry entry exists. composeTitle
trims a long subject line at a clause boundary to fit a link preview.
- Doc pages carry that subject line too, because every spec's H1 is just
its name: "OpenStream" alone was the whole title.
- docExcerpt joins the wrapped lines of the first real paragraph and
skips Status:/Slug: headers and list lead-ins, so descriptions stopped
being "Status: 0.1 draft" and "Core tables:".
- contract/page-metadata.contract.test.ts fails if a sitemap route has no
title, if a spec page has no registry entry, if two pages share a
title, if a page declares metadata without the builder, or if a page
loses its social image.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
openicon.json with the hq block, 259 full-colour UI icons as WebP 64/128
and 111 brand SVGs in their owners' colours. The gallery's Simple/HQ
toggle appears on its own now that icons carry hq.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* OpenEmoji catalog: browse and install by network
Reads platforms.json from the set: a network picker grouped by kind
(custom emoji, sticker packs, images to post), and for the chosen
network its verified limits, install steps, packs with sizes, Show (in
the grid) and Download (release assets), the grid narrowed to what its
bundles hold with each emoji's name on that network, and the detail
panel saying which pack holds it. A network that names what it is for
opens on it: X opens on country flags.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* OpenEmoji catalog: previews for 2,424 drawn glyphs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A set may add a full-colour HQ style beside the canonical simple one:
styles: [simple, hq], a top-level hq block (sizes, webp_sizes, provenance,
coverage) and an hq block per icon (png, webp, svg for brands, made_by,
hex, hex_source). Same silhouette as simple, falls back to simple, brand
colour must be the owner's with its source, provenance is per style.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replaces the 47-icon sample with the whole reference set: search by name,
alias or keyword; category chips with counts; UI/brand filter; SVG,
Nerd Font, Unicode or ASCII view (the Nerd Font view uses the official
symbols subset to the set's 349 glyphs, 38 KB woff2, MIT); size and
colour; a detail panel with all three glyphs and codepoints, trademark
notes, copy SVG / <img> / icon("key"), and downloads. Ready for the
optional HQ (full-colour) style: a Simple/HQ toggle appears once icons
carry an hq field. Filters live in the URL.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One openicon.json naming every icon (kebab-case keys, unique aliases,
shared keywords), 24x24 currentColor SVGs with a stated grid, and three
terminal glyphs per icon: a Nerd Font character with its codepoint and
glyph name, a Unicode symbol and a 1-4 character ASCII spelling, picked
nerd > unicode > ascii. Brand logos carry brand: true, a trademark note,
source and licence. made_by + W3C AI disclosure per set and per icon.
Mapping from Lucide, Tabler, Font Awesome, Nerd Fonts and Simple Icons.
Landing page shows 47 icons from the reference set (profullstack/openicon).
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* OpenEmoji catalog: every emoji in the reference set, with search and filters
/openemoji/catalog reads the set's openemoji.json (every Unicode emoji,
drawn or not) and filters by name and CLDR keyword, group, subgroup,
skin tone (keyboard-style swap), Emoji version and status, with sort,
tile size, URL-synced filters, paged rendering and a detail panel
(codepoints, tone family, copy, PNG/SVG downloads, keyword chips).
Spec: an entry without files is listed, not drawn.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* OpenEmoji catalog: reference set previews, first 206 glyphs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One openemoji.json that states coverage against a Unicode version, the
licence, and who or what drew the set (made_by plus the W3C AI disclosure
vocabulary), and glyphs named by fully-qualified codepoint sequence.
Custom emoji get x- keys with shortcodes; skin tones name their base;
rendered emoji keep the character as alt. Mapping from Mastodon, Slack,
Discord and CLDR.
Landing page shows 19 glyphs drawn by the reference implementation,
`emoji` in profullstack/cli-tools.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every secret now has a category derived from its name (db, social, server,
api, cloud, finance, crypto, ai, email, messaging, storage, dns, analytics,
devtools, auth, config, other). One rule table in @logicsrc/opencreds serves
both vaults; services win over generic words, so STRIPE_WEBHOOK_SECRET is
finance, not auth. Checked against the 1,208 distinct key names in the
profullstack team: 74 fall to "other".
Team vaults (where the shared .env secrets live):
- teams categories [team] the filter words, with per-category counts
- teams secrets <team> [project] [env] --category/-c --search/-s
names + categories, never decrypts; --format csv
- teams export <team> [project] [env] --category -o file.csv [--yes]
decrypts into team,project,env,category,key,
value,updated_at (0600); skips vaults without a
grant and names them
Personal vault (OpenCreds):
- vault list --category, and the category column in list output
- vault export --format csv: one flat row per item, keeps key/account
secrets that a Bitwarden CSV drops; --category on every export format
DX:
- examples in `logicsrc vault help` / -h / --help that start by saying which
of the two vaults you want, plus examples on teams and each subcommand
- password prompts go to stderr, so eval "$(logicsrc vault unlock)" works
- hints name the command you actually ran (logicsrc vault init, not
opencreds init)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TITLE / STYLE / EXCLUDE STYLES / LYRICS label blocks with [Section - direction]
tags, no Markdown (Suno reads plain text only). Kept beside the audio as the
same name with .txt, album lyrics.txt with ==== dividers, one-way mapping to
ID3/Vorbis tags. Example is track 002 of Þrøngva, When the Ravens Lied.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Power Key bought once at profullstack.com/shop now opens this site:
the gateway verifies it offline with @profullstack/keys and treats it
as a pass, so it is never charged as a crawler or metered. The 402
body and the sales page point at the shop.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
OpenObject is a bucket you can mount: keyed objects as ipfile swarms placed
on OpenDisk disks under pay2seed at a stated redundancy (three replicas on
three operators in two countries by default), an ipdb index as the bucket's
clock, a repair loop, an HTTP API, an S3 mapping and a mount whose
consistency is close-to-open by seq. d1sks.com is the reference store.
OpenSlice is a container whose compute is rented from one market and whose
disk is mounted from another: a host descriptor at
/.well-known/openslice.json, one signed slice file (image by digest,
OpenCPU/OpenMemory/OpenGPU units, OpenObject mounts, OpenCreds env, ports,
placement, lifetime), and a reservation that is a paid2seed lease applied to
compute with presence proofs per epoch. slic3s.com is the reference
marketplace; c0mpute hosts take the slice role.
Both are registered under OpenServer in the catalogs family with landing
pages, rows in the OpenSwarm family table, and the spec-discovery contract.
llms.txt now cites the specification URL for child specs too, which every
block under OpenServer had been missing.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
A new LogicSRC spec in the catalogs family. One heading, an identity block
(Kind, Web, Repo, Operator, License, Extends, Updated), one line, then eleven
layers: Languages, Runtimes, Interfaces (one ### per way in: web, api, cli,
tui, mcp, desktop, mobile, worker, extension, bot), Data, Services, Modules,
Tooling, Hosting, Auth, Conventions, Not. An item is one bullet: name,
version, an optional status word (trial, hold, leaving) and a role. Extends
inherits a parent file, sections replace, Conventions and Not accumulate.
Rule 8 is the reading rule for agents: use what is listed, prefer listed
over new, ask before adding a layer or a service, never add a Not, keep the
file true. Discovery at OpenStack.md in the repo, /.well-known/openstack.md,
rel=openstack, or a platform path. JSON is derived and never the source.
logicsrc.com serves its own at /.well-known/openstack.md: the route extracts
the worked example from docs/openstack.md, and a contract test holds the two
together and checks the file follows its own rules. rel=openstack in <head>
and in the Link header beside openprofile.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The W3C AI Content Disclosure community group defines four attributes,
not one: ai-disclosure, ai-model, ai-provider and ai-prompt-url. Both
specs carried only the first, as `disclosure`. They now carry all four,
as `disclosure`, `ai_model`, `ai_provider` and `ai_prompt_url`: which
model, whose, and a page describing how it is used, optional and
meaningful only beside a disclosure other than none. `mixed`, the value
the group's meta tag form allows, is accepted at the site level in
OpenWebring and the page level in OpenWiki.
A ring host copies all of them into the member entry as the member said
them. A wiki carries them on the page and per revision, and a host
rendering a page puts the same four on it as the group's meta tag and
attributes. Both specs are 0.1.1; the wire version stays 0.1, since every
field is additive and absent is still unstated.
Claude-Session: https://claude.ai/code/session_01XYae2mH3khdwiXUVzcVMDw
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
OpenWiki writes down what the [[Page]] convention already is in practice,
so a wiki can be moved, mirrored, read by a directory and edited by an
agent without reading any engine's source. One Markdown file per page
with a small optional front matter (title, aliases, tags, summary,
author, made_by, disclosure, created, updated, license, lang, redirect);
wikilinks resolved by exact name, loose name, alias, path, then wanted;
one address with three representations (rendered, .md, .md?rev=) and a
history.json per page; a descriptor at /.well-known/openwiki.json with
pages_url, changes (Atom), repo, edit, editors and accepts; editing as
PUT of the same file with If-Match on the revision; discovery; what a
directory owes a wiki. Every revision says who made it: human, ai or
both, the same word OpenWebring uses.
A folder that follows it opens unchanged in Obsidian and Logseq, and any
such folder becomes an OpenWiki by adding the two files. The first host,
goviral.wiki, is being built on it.
Claude-Session: https://claude.ai/code/session_01XYae2mH3khdwiXUVzcVMDw
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Manual publish of the report attached to the nixamp v0.24.2 release
(openstream-report-0.24.2.json/.md). The release workflow's
publish-report job skipped because LOGICSRC_PUBLISH_TOKEN is not set
on profullstack/nixamp. Built-in synthetic corpus, so the id carries
the -synthetic label per docs/openstream/reports/README.md.
Claude-Session: https://claude.ai/code/session_015uheT4Gn9BBKAnSZGcabyN
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
OpenL10n is the record of what a file says, in any language: one
transcript per media per language, addressed by the OpenFile id (or a
url: or live: identity for what has no bytes), lines as seconds into the
media, a translation kept beside the original and marked with what it
came from, complete or heard in pieces, made once on request with 202
progress, served as JSON, SRT, VTT or text.
OpenI18n is one file a service serves at /.well-known/openi18n.json
about the languages it speaks: which, which it can turn into which, how
to ask for one, and where texts are translated, with limits, models and
the pivot said out loud.
Both are what nixamp 0.24 and 0.25 serve today, written down so anything
that hears or translates can keep the same record.
Claude-Session: https://claude.ai/code/session_015KAKuRngFbQg3kET6RS5yN
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The spec and its landing page now name the directory that reads every
OpenWebring host: one row per ring and one per member, with made_by as
the member said it and the status the host last verified, re-read hourly.
The first host line also names rssamplifier's curated Profullstack ring.
Claude-Session: https://claude.ai/code/session_01XYae2mH3khdwiXUVzcVMDw
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* OpenFleet reference implementation: @logicsrc/openfleet 0.1.0 and logicsrc fleet
Ship what docs/openfleet.md describes. The new workspace package holds the
record (write once, never overwrite, 0600), the ledger (append-only JSON
Lines, merged across ledger*.jsonl by at), the ceiling rules (whole fleet
ceiling, narrowed swarm keys, a merge that never widens, refusals by key),
claiming and deriving exactly as the spec's "Claiming and deriving" and
rule 13, and fold(), which turns any $OPENFLEET_HOME plus the engine
rosters into the tree the landing page shows.
logicsrc fleet open|cap|tree|stop|log are the sysop's verbs, every one with
--json. open and cap exit 4 when OPENFLEET_MEMBER is set; stop exits 4
outside the caller's subtree, ends nested swarms first, goes through each
member's own engine (claude stop, moshcode herd kill, tmux kill-pane, a
signal for claude-p) and writes one swarm.end per swarm. tree reads claude
agents --json --all and ~/.moshcode/herd/sessions.json when it can, draws
recordless sessions as roster roots of the implicit fleet, and writes
member.end lost for a recorded member its engine no longer lists.
Claude Code takes part through hooks: logicsrc fleet hooks install merges
SessionStart, UserPromptSubmit, PreToolUse, Stop and SessionEnd into
~/.claude/settings.json without clobbering it, and logicsrc fleet hook
<Event> runs each one. SessionStart claims, derives or writes a root record
and hands the member its variables through CLAUDE_ENV_FILE; UserPromptSubmit
checks the ceiling with the permission mode the engine reports and writes
member.start, or refuses the first prompt with exit 2 and ceiling.refuse;
PreToolUse denies an edit outside piece.owns; Stop and SessionEnd write
member.end. A hand-started root takes the engine's reported approvals
before member.start, since the command line only guesses them. Hooks
never fail the engine: everything is caught and logged to hooks.log.
The spec and the landing page now say what ships, keep Status 0.1, and
record the two verified Claude Code limits: a background job dispatched from
claude agents gets no launcher environment, and OPENFLEET_* exported at
SessionStart reach the member's tools but not later hooks, so hooks key on
session_id through $OPENFLEET_HOME/sessions/<session_id>.json. PRD 0008
covers the work. CLI 0.2.1 -> 0.3.0; build and build:cli chains build the
package before the CLI; README and docs/cli.md list the group.
Tests: 95 in the package (record, ledger merge, every narrower case, the
worked example's claim and derive, the folded tree, hook install
idempotence, each hook handler including the exit-2 refusal and the
PreToolUse deny, every verb with fake deps) and 4 in the CLI.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
* OpenFleet fix round: rebuild the ceiling from the ledger, once-markers, rule 6 in tree, lost only for what a roster can hold
The review of the reference implementation against moshcode found the two
readers disagreeing on the same files. This round applies the shared
rulings so both sides read a ledger the same way.
Ceiling (R-A, R-B, R-C, R1, R6, R10, R15, R17): memberCeiling rebuilds the
effective ceiling from the ledger on every read. The latest fleet-target
fleet.cap (else fleet.open, else the implicit fleet's) replaces the copy in
a record, so a sysop's widening cap reaches running members; then each
swarm.spawn narrowing down the path, then swarm caps last. In the implicit
fleet a parentless record's own approvals enters at the root; a ceiling a
writer left without the key is never read as native, and startMember fills
it with the engine's word while the record is unclaimed. A fleet.open or
cap with no hosts means the host it was written on (R23).
Once-markers (R-G, R28): member.start, member.end and swarm.end each take
an exclusive create under fleets/<fleet>/marks/<event>.<id> before the
append; a lost end takes <id>.lost so a real end can still supersede it.
The hooks let a real end follow a lost line (R9).
tree (R-F, R20): run by the sysop it enforces rule 6, stopping a member
past its effective until with state timeout and the members of a swarm or
fleet at its budget with state budget, then writes swarm.end for each swarm
touched once it is complete. An agent's tree stops nothing. lost is written
only for a member its engine's roster can hold: a claude-code background job
(8-hex member or session) or a moshcode pane, never an interactive session
claude agents does not list (R-E, R3, R14). A nested swarm is drawn under
the member that spawned it and its row shows the effective ceiling (R25).
stop and cap (R-D, R-H, R22, R27): swarm.end is written only once every
member and every nested swarm has an end line that counts; an engine that
will not end a member leaves it without an end line and the verb exits
non-zero. claude stop takes the job id: the member of a background job,
else the first eight characters of a session UUID; an interactive session
with no job id cannot be stopped and the tool says so. cap on a swarm
refuses a key that would widen. A derived claude-code job is named by its
job id and carries no pid.
Also: R-I (endMember ends only the engine-minted swarm of one), R35 (a
derived record's guessed approvals corrected at UserPromptSubmit), R32
(the UserPromptSubmit hook passes only exit 2 through), R31 (package
README), R36 (rule 13 says the launcher test is unimplemented in 0.1),
docs and PRD 0008 updated for lost, rule 6 and the markers. 113 openfleet
tests, 93 CLI tests, contract green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
* openfleet hooks: no member.end for a member that never started
A first prompt refused by the ceiling still lets the session wind down through Stop and SessionEnd; those handlers now write nothing when the ledger holds no member.start for the member, so a refused member is never drawn as done.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
* logicsrc-mcp test: the next free PRD id is 0009 now that PRD 0008 exists
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The footer carries the three OpenWebring links (previous, ring, random,
next, with ?from=https://logicsrc.com/blog, the member URL the directory
holds), and /.well-known/openwebring.json says who makes the blog: both,
ai-generated, in the W3C AI Content Disclosure vocabulary the spec adopts.
Claude-Session: https://claude.ai/code/session_01XYae2mH3khdwiXUVzcVMDw
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
PR 177 shipped a second OpenFleet under the same slug: a published listing of OpenAgent profiles and ipfile swarms with CoinPay rental offers. Anthony ruled that OpenFleet means the human-controlled fleet and the record of who spawned whom, so the rental contract is renamed OpenRental (slug openrental, catalogs family, group noun listing): docs/openrental.md, logicsrc-openrental.schema.json with type logicsrc.openrental and scope kind listing, fixtures/openrental, createOpenRental and OpenRental* types in the SDK, the openrental validator kind. Minor bumps because an export moved: @logicsrc/schemas 0.2.0, @logicsrc/validators 0.2.0, @logicsrc/sdk 0.2.0. The discovery contract test now covers openfleet, openrental and openwall, one per family, and accepts a landing-page link in llms.txt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV