diff --git a/apps/logicsrc-web/src/app/llms.txt/route.ts b/apps/logicsrc-web/src/app/llms.txt/route.ts index b36e6fd..af94c3f 100644 --- a/apps/logicsrc-web/src/app/llms.txt/route.ts +++ b/apps/logicsrc-web/src/app/llms.txt/route.ts @@ -24,6 +24,12 @@ 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. +- [OpenCPU](${SITE_URL}/opencpu): The compute block of an OpenServer offer: threads against cores, the processor by its vendor name, dedicated, shared or burstable allocation, and a range that says what a buyer can add at checkout and for how much. +- [OpenMemory](${SITE_URL}/openmemory): The memory block of an OpenServer offer: RAM in mebibytes, DDR generation, ECC as three states, reserved or balloonable allocation, and a range priced per step. +- [OpenGPU](${SITE_URL}/opengpu): The gpu block of an OpenServer offer: the card by its vendor name, count and VRAM per device, interconnect, passthrough, MIG, vGPU or shared access, and a range over count. +- [OpenBandwidth](${SITE_URL}/openbandwidth): The network block of an OpenServer offer: port speed, transfer, unmetered, 95th percentile or flat metering, overage, IPv4 and IPv6 addresses as a priced resource, DDoS scrubbing, and a range. +- [OpenFile](${SITE_URL}/openfile): One file a publisher serves about the files it has published: content hash, swarm and HTTP fetch routes, verification, consent basis, price, and who holds it now, discovered at /.well-known/openfile.json. The web door onto an OpenSwarm ipfile swarm. +- [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. - [AgentSwarm](${SITE_URL}/agent-swarm): Provider-neutral agent orchestration, model routing, and cost controls. diff --git a/apps/logicsrc-web/src/app/openbandwidth/page.tsx b/apps/logicsrc-web/src/app/openbandwidth/page.tsx new file mode 100644 index 0000000..f689740 --- /dev/null +++ b/apps/logicsrc-web/src/app/openbandwidth/page.tsx @@ -0,0 +1,63 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { ResourceSpecPage, type ResourceSpec } from "@/components/resource-spec-page"; + +export const metadata: Metadata = { + title: "OpenBandwidth · LogicSRC", + description: + "OpenBandwidth is the network block of an OpenServer offer: port speed, how traffic is metered (transfer, unmetered, 95th percentile, flat), what overage costs, IPv4 and IPv6 addresses as a priced resource, DDoS scrubbing, and a range that says what a buyer can add at checkout and for how much.", + alternates: { canonical: "/openbandwidth" } +}; + +const SPEC: ResourceSpec = { + name: "OpenBandwidth", + slug: "openbandwidth", + block: "network", + tagline: + "The network line of a server purchase, with the words fixed: the port, the meter, the overage, the addresses, and what more of any of them costs.", + problem: + "Unmetered 1 Gbps, 1 Gbps with 20 TB, and 1 Gbps at the 95th percentile with 100 Mbps committed are three products that every comparison site shows as 1 Gbps. The surprise on the invoice is always the network line: overage at a cent a gigabyte here and nine there, ingress free or billed, a second IPv4 address at two a month or unavailable at any price. IPv4 is now a market of its own, leased by the /24, and no catalog format has a place for it.", + sample: `{ + "network": { + "bandwidth_mbps": 1000, + "metering": "transfer", + "transfer_gb": 20000, + "counts": "egress", + "overage": { "amount": 0.01, "currency": "USD", "per_gb": 1 }, + "ipv4": 1, + "ipv4_price": { "amount": 2, "currency": "USD", "interval": "month", "per": 1 }, + "ipv4_max": 8, + "ipv6": "/64", + "ddos": "always-on", + "range": { + "key": "bandwidth_mbps", "min": 1000, "max": 10000, "step": 1000, + "price": { "amount": 15, "currency": "USD", "interval": "month", "per": 1000 } + } + } +}`, + smallest: `{ "network": { "bandwidth_mbps": 1000 } }`, + fields: [ + ["bandwidth_mbps", "integer, required", "The port speed sold. A shaped link states the shaped rate."], + ["metering", "transfer, unmetered, percentile, flat", "Gigabytes against transfer_gb, no cap, 95th percentile with commit_mbps, or a fixed price for the port."], + ["transfer_gb, counts", "integer per interval; egress, ingress, both, max", "Included traffic and which direction counts. A reader never assumes egress."], + ["overage, over_cap", "amount, currency, per_gb or per_mbps; bill, throttle, suspend", "What traffic past the cap costs, or what happens instead."], + ["ipv4, ipv4_price, ipv4_max, ipv6", "integer; price per address; integer; boolean or prefix", "Addresses as a resource: included, the price of more, the ceiling, and the IPv6 assignment."], + ["ddos, private_network, uplinks", "always-on, on-demand, none; boolean; integer", "That mitigation exists, a private LAN, physical links on a dedicated box."], + ["range", "key bandwidth_mbps, transfer_gb or ipv4; min, max, step, price", "What the buyer can dial and what a step costs."] + ], + directory: [ + ["Meter beside the port", "1 Gbps unmetered and 1 Gbps with 20 TB are two rows with two words."], + ["Price a month of traffic", "Base plus overage at a stated volume, with the volume shown."], + ["Addresses as a resource", "Included count, price of more, ceiling. Zero with no price is unstated, not free."], + ["Never assume direction", "A cap with no counts is a cap with the direction unstated."] + ], + absent: [ + ["No speed test", "bandwidth_mbps is the port sold. Achieved throughput is a measurement."], + ["No carriers or peering", "Upstreams and exchanges are the provider's network page. An ASN goes under the provider's own key."], + ["No SLA", "Uptime percentages and credits are the provider's terms, linked from the offer's url."] + ] +}; + +export default function OpenBandwidthPage(): ReactNode { + return ; +} diff --git a/apps/logicsrc-web/src/app/opencpu/page.tsx b/apps/logicsrc-web/src/app/opencpu/page.tsx new file mode 100644 index 0000000..67beacc --- /dev/null +++ b/apps/logicsrc-web/src/app/opencpu/page.tsx @@ -0,0 +1,61 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { ResourceSpecPage, type ResourceSpec } from "@/components/resource-spec-page"; + +export const metadata: Metadata = { + title: "OpenCPU · LogicSRC", + description: + "OpenCPU is the compute block of an OpenServer offer: threads against cores, the processor by its vendor name, dedicated, shared or burstable allocation, and a range that says how many more a buyer can add at checkout and for how much.", + alternates: { canonical: "/opencpu" } +}; + +const SPEC: ResourceSpec = { + name: "OpenCPU", + slug: "opencpu", + block: "compute", + tagline: + "The processor line of a server purchase, with the words fixed: threads or cores, whose they are, and what one more costs.", + problem: + "Four vCPU on one pricing page is four hyperthreads of a shared socket, throttled at a fifth of sustained load. On the next it is four dedicated cores with the boost clock quoted. A comparison site sorts both on the number four, and the buyer who wanted the second pays for the first. A configurator that offers three processors at three prices keeps that choice in a form where no reader can see it.", + sample: `{ + "compute": { + "vcpu": 4, + "cores": 2, + "threads_per_core": 2, + "arch": "x86_64", + "model": "AMD EPYC 9354", + "vendor": "AMD", + "base_ghz": 3.25, + "boost_ghz": 3.75, + "allocation": "dedicated", + "range": { + "key": "vcpu", "min": 2, "max": 32, "step": 2, + "price": { "amount": 4, "currency": "USD", "interval": "month", "per": 1 } + } + } +}`, + smallest: `{ "compute": { "vcpu": 4 } }`, + fields: [ + ["vcpu / cores", "integer, one required", "Threads sold to the guest, or physical cores. threads_per_core relates them; a reader never derives one from the other without it."], + ["arch", "x86_64, arm64, riscv64, or the provider's word", "Absent means unstated, not x86."], + ["model, vendor", "the vendor's own name", "AMD EPYC 9354, Ampere Altra Max. Kept verbatim so a buyer can look it up."], + ["base_ghz, boost_ghz, sockets", "decimal GHz, integer", "The vendor's figures, not a measurement."], + ["allocation", "dedicated, shared, burstable", "Reserved for the buyer, oversubscribed, or shared with a credit balance (credits_per_hour, baseline_pct)."], + ["range", "key, min, max, step, price", "What the buyer can dial at checkout and what each step costs on top of the base price."] + ], + directory: [ + ["Two columns", "vcpu and cores are sorted separately; a row with neither is listed and marked unstated."], + ["Allocation beside the count", "Four dedicated threads and four shared ones are different products at the same number."], + ["Price the range", "Base price plus the per-step price, so 8 vCPU reads as base plus four steps."], + ["Keep the model string", "Normalise for search, display the vendor's spelling."] + ], + absent: [ + ["No benchmarks", "Clock figures are the vendor's. A measured score is another document's business."], + ["No instruction-set flags", "AVX-512, SVE and the rest: look up model. A provider that wants them states them under its own key."], + ["No scheduling guarantees", "Pinning, NUMA and latency are the provider's terms, linked from the offer's url."] + ] +}; + +export default function OpenCpuPage(): ReactNode { + return ; +} diff --git a/apps/logicsrc-web/src/app/opendisk/page.tsx b/apps/logicsrc-web/src/app/opendisk/page.tsx new file mode 100644 index 0000000..7cf8230 --- /dev/null +++ b/apps/logicsrc-web/src/app/opendisk/page.tsx @@ -0,0 +1,195 @@ +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: "OpenDisk · LogicSRC", + description: + "OpenDisk is one file a machine serves about the disk it will rent: free GiB, price per GiB-month, location, policy, proof cadence and hub standing, at /.well-known/opendisk.json. What a peer-to-peer storage market is made of. Reference marketplace d1sks.com.", + alternates: { canonical: "/opendisk" } +}; + +const DESCRIPTOR = `{ + "name": "seeder-a41e, Falkenstein", + "operator": "https://seeder-a41e.example/.well-known/openprofile.md", + "key": "ed25519:a41e…7b0a", + "hubs": ["https://bittorrented.com/api/openswarm", "https://d1sks.com/api/openswarm"], + "capacity": { "total_gib": 3726, "free_gib": 2210 }, + "price": { "currency": "USD", "per_gib_month": 0.12, "per_gib_transfer": 0.004, "max_gib": 2000, "max_days": 365 }, + "location": { "regions": ["fsn1"], "countries": ["DE"] }, + "network": { "bandwidth_mbps": 1000, "transfer_gb": 20000 }, + "accepts": { "visibility": ["private", "public"], "encrypted_only": false, "max_file_gib": 500 }, + "proof": { "kinds": ["challenge", "probe"], "every_hours_min": 6 }, + "record": { "source": "https://bittorrented.com/api/openswarm/pay2seed/seeders/ed25519:a41e…7b0a", "standing": 412 }, + "payout": { "payee": "ed25519:a41e…7b0a", "network": "eip155:8453" } +}`; + +const MINIMAL = `{ "name": "spare drive, Lisbon", "capacity": { "free_gib": 900 }, + "price": { "currency": "USD", "per_gib_month": 0.08 } }`; + +const RENT: Array<[string, string]> = [ + ["1. Read", "The descriptor for policy and price, and record.source at the hub for standing."], + ["2. Attest", "The consent record pay2seed requires, with README.md at the root of the swarm."], + ["3. Offer", "A pay2seed offer at one of the disk's hubs, at or above per_gib_month, with the budget escrowed."], + ["4. Lease", "The disk's seeder client takes it on its next poll, or at once when the hub pushes to proof.webhook."], + ["5. Prove", "Challenges and probes every period, receipts per proven period, payout to the payee. All paid2seed, unchanged."] +]; + +const MAPPING: Array<[string, string]> = [ + ["offers[].kind", "storage"], + ["offers[].model", "p2p for a peer, centralized for a provider; the disk may say"], + ["offers[].price", "{ amount: per_gib_month, currency, interval: month, unit: gib }"], + ["offers[].stock", "in_stock while free_gib covers min_gib"], + ["location, network, storage", "the same keys, the same units"] +]; + +const ABSENT: Array<[string, string]> = [ + ["No lease in this file", "The descriptor is the ask, the offer is the bid, the lease is the match, made at the hub as paid2seed says."], + ["No escrow at the disk", "Money sits at a hub. A disk never holds a requester's funds."], + ["No reputation of its own", "record is a copy of what a hub signed. A marketplace reads the hub, and lists the hub's number."], + ["No plaintext", "By default a disk holds ipfile ciphertext it cannot read, and a personal swarm's existence is not in its public list."], + ["No central registry", "Anyone may serve a descriptor and anyone may read it. Two marketplaces reading the same file list the same disk."] +]; + +export default function OpenDiskPage(): ReactNode { + return ( + +
+
+

LogicSRC standards surface

+

OpenDisk

+

+ One file a machine serves about the disk it will rent, so a disk can be found instead of + waited for, and a peer with a spare terabyte is on the market by putting a file at a URL. +

+
+

+ OpenSwarm already says how a seeder is paid to hold a swarm: + an offer with money escrowed, a lease, a storage proof every period, a payout per + GiB-month. What it does not say is how a requester finds a seeder before posting. The + market is one-sided: offers are listed and seeders poll them. OpenDisk is the other side. + A disk puts its free space, price, location, policy, proof cadence and hub standing at{" "} + /.well-known/opendisk.json, and a requester reads the disk + before it posts. +

+

+ Status: 0.1. d1sks.com is the reference marketplace: every + descriptor it has read, standing pulled from the hubs each disk names, and an offer form that + posts to the disk's hub. Every disk is also a hosting offer in the{" "} + OpenServer sense, listed as one at nichedb.dev. +

+
+ +
+
+

The descriptor

+

+ Served at /.well-known/opendisk.json. Only a name, the free + space and a price are required. +

+
+
{DESCRIPTOR}
+

+ key is the seeder identity that signs proofs and is paid;{" "} + hubs is where it takes leases. accepts{" "} + is the operator's policy stated up front, so nobody posts an offer this disk would never + take. record.source is the hub's own page for this seeder, + and a marketplace reads the hub, not the file, for anything it ranks on. The smallest valid + descriptor: +

+
{MINIMAL}
+
+ +
+
+

Renting a disk

+

Nothing on the swarm side changes. The descriptor only moves the first step to a URL.

+
+ + + {RENT.map(([step, what]) => ( + + + + + ))} + +
+ {step} + {what}
+
+ +
+
+

As an OpenServer offer

+

A disk is a hosting offer, and a directory that reads OpenServer lists it without a second parser.

+
+ + + + + + + + + {MAPPING.map(([key, from]) => ( + + + + + ))} + +
OpenServerfrom OpenDisk
+ {key} + {from}
+

+ A provider that sells storage plans and rents disk to swarms serves both files. A peer with + one drive serves only this one, and is listed in both places. +

+
+ +
+
+

What is deliberately absent

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

Where everything lives

+
+
    +
  • + Specification: the descriptor, holding, renting a disk, + marketplaces, the OpenServer mapping +
  • +
  • + d1sks.com: the reference marketplace +
  • +
  • + OpenSwarm: pay2seed for the offer, paid2seed for leases and + proofs, ippay for the payee, ipfile for what is held +
  • +
  • + OpenFile, the publisher's side and the holders list a disk + appears in; OpenServer, the hosting offer a disk maps onto +
  • +
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/app/openfile/page.tsx b/apps/logicsrc-web/src/app/openfile/page.tsx new file mode 100644 index 0000000..51c4815 --- /dev/null +++ b/apps/logicsrc-web/src/app/openfile/page.tsx @@ -0,0 +1,220 @@ +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: "OpenFile · LogicSRC", + description: + "OpenFile is one file a publisher serves about the files it has published: content hash, swarm and HTTP fetch routes, verification, consent basis, price, and who holds it now, at /.well-known/openfile.json. The web door onto an OpenSwarm ipfile swarm.", + alternates: { canonical: "/openfile" } +}; + +const DESCRIPTOR = `{ + "publisher": { + "name": "Dartmoor Field Recordings", + "operator": "https://dartmoor.example/.well-known/openprofile.md", + "key": "ed25519:5d29…d59e", + "hubs": ["https://bittorrented.com/api/openswarm"] + }, + "files": [{ + "id": "sha256:d6c3…93ff", + "name": "interview-2026-09-05.flac", + "size": 734003200, + "swarm": { "file": "ed25519:0d87…295d", "infohashV2": "sha256:4b74…a342" }, + "encryption": "ipfile", + "fetch": [ + { "kind": "ipfile", "url": "magnet:?xs=urn:btpk:0d87…295d&s=ipfile" }, + { "kind": "http", "url": "https://gw.c0mpute.com/file/d6c3…93ff" }, + { "kind": "hls", "url": "https://gw.c0mpute.com/file/d6c3…93ff/index.m3u8" } + ], + "attestation": { "basis": "own", "license": "CC-BY-4.0" }, + "price": { "amount": 0.5, "currency": "USD", "per": "key", "offer": "https://keys.dartmoor.example/…" }, + "holders": "https://bittorrented.com/api/openswarm/files/d6c3…93ff/holders" + }] +}`; + +const MINIMAL = `{ "publisher": { "name": "Dartmoor Field Recordings" }, + "files": [{ "id": "sha256:d6c3…93ff", "name": "interview-2026-09-05.flac" }] }`; + +const FETCH: Array<[string, string, string]> = [ + ["ipfile", "a magnet for an OpenSwarm client", "encrypted pieces, paid per piece, the key on a pass"], + ["magnet", "a vanilla magnet", "the ciphertext, for a client that cannot pay"], + ["webseed", "BEP 19, ciphertext by range", "a gateway standing in for peers"], + ["http", "the plaintext by range from a gateway", "a browser with nothing installed, behind a pass when there is a price"], + ["hls", "a media file as a standard playlist", "any player, sealed when there is a price"] +]; + +const REUSED: Array<[string, string]> = [ + ["The swarm and the manifest", "ipfile: the file key, both infohashes, plainRoot as the content hash, the plaintext piece layer"], + ["Consent and the README", "pay2seed: the attestation record, its basis and licence, the notice endpoint, README.md at the root"], + ["Who holds it", "paid2seed: leases and the proofs behind provenAt"], + ["Payment", "ippay: a pass bought over x402, presented as a bearer token on the gateway"], + ["The feed", "ipdb: the same catalogue, replicated between peers, for a reader that speaks the swarm"] +]; + +const ABSENT: Array<[string, string]> = [ + ["No new swarm format", "The swarm is ipfile, the consent is pay2seed's, the payment is ippay's. This is a JSON door onto records that already exist."], + ["No search", "A descriptor lists one publisher's files. Finding a file across publishers is a directory's job."], + ["No trust score", "verified says where the file came from and who signed the manifest. basis is what the publisher claimed. The rest is the reader's judgement."], + ["No DRM", "A pass holder gets the key and the bytes, as ipfile says."] +]; + +export default function OpenFilePage(): ReactNode { + return ( + +
+
+

LogicSRC standards surface

+

OpenFile

+

+ One file a publisher serves about the files it has published, so a file can be found + instead of crawled, and fetched by a browser that has never heard of a swarm. +

+
+

+ A file on a swarm is findable by its infohash and by nothing else. OpenSwarm{" "} + fixed the inside of the swarm: a signed manifest, encrypted pieces, consent at upload, a + replicated catalogue. What none of that gives a person with a browser, a search engine or a + directory is a URL to start from. OpenFile puts a publisher's catalogue at{" "} + /.well-known/openfile.json, with a content hash to verify + against, every way to fetch the bytes, the basis it was published on, the price, and who is + holding it right now. +

+

+ Status: 0.1. The reference reader is bittorrented.com, which lists consented swarms beside + the bare infohashes of its DHT crawl, with the README as the page. A product for it is + planned under a name not yet chosen. +

+
+ +
+
+

The descriptor

+

+ Served at /.well-known/openfile.json. Only the publisher's + name and each file's content hash and name are required. +

+
+
{DESCRIPTOR}
+

+ id is the SHA-256 of the plaintext, which for an ipfile swarm is + the manifest's plainRoot: the same root the decrypted file + verifies against. Two publishers serving the same bytes list the same id, and a directory + has one file with two listings. encryption is ipfile unless the + publisher says none as an explicit act. The smallest valid + descriptor: +

+
{MINIMAL}
+
+ +
+
+

Five ways to fetch

+

Listed in the publisher's order of preference. A reader picks the first kind it speaks.

+
+ + + + + + + + + + {FETCH.map(([kind, what, who]) => ( + + + + + + ))} + +
kindWhat it isWho uses it
+ {kind} + {what}{who}
+

+ Over any of them the bytes verify the same way: each piece against the plaintext piece + layer, or the whole against id. A reader refuses bytes that fail + and says which holder served them. +

+
+ +
+
+

Holders

+

A file is only as available as the machines holding it.

+
+

+ holders answers with who has the bytes now: a{" "} + seeder with a lease and the time it last passed a proof, a{" "} + gateway serving it over HTTP, a peer{" "} + merely seen. Each carries its age, and a seeder's lease can be checked at the hub. + A holder that rents disk links its own OpenDisk descriptor, so a + publisher that likes a holder can buy more of it. +

+
+ +
+
+

What is reused

+

Every record OpenFile points at already has a name in the OpenSwarm family.

+
+ + + {REUSED.map(([what, from]) => ( + + + + + ))} + +
+ {what} + {from}
+
+ +
+
+

What is deliberately absent

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

Where everything lives

+
+
    +
  • + Specification: the descriptor, the per-file descriptor, + holders, discovery, fetching and verifying, what a directory owes a publisher +
  • +
  • + OpenSwarm: ipfile, pay2seed, paid2seed, ippay and ipdb, + the records this file is a door onto +
  • +
  • + OpenDisk, what a holder serves about the disk it rents;{" "} + OpenServer, how a gateway or seeder is listed as hosting +
  • +
  • + OpenProfile.md, the operator behind a publisher +
  • +
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/app/opengpu/page.tsx b/apps/logicsrc-web/src/app/opengpu/page.tsx new file mode 100644 index 0000000..a35ae86 --- /dev/null +++ b/apps/logicsrc-web/src/app/opengpu/page.tsx @@ -0,0 +1,61 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { ResourceSpecPage, type ResourceSpec } from "@/components/resource-spec-page"; + +export const metadata: Metadata = { + title: "OpenGPU · LogicSRC", + description: + "OpenGPU is the gpu block of an OpenServer offer: the card by its vendor name, count and VRAM per device, the interconnect between them, passthrough, MIG, vGPU or shared access, and a range that says how many more a buyer can add at checkout and for how much.", + alternates: { canonical: "/opengpu" } +}; + +const SPEC: ResourceSpec = { + name: "OpenGPU", + slug: "opengpu", + block: "gpu", + tagline: + "The accelerator line of a server purchase, with the words fixed: which card, how many, how much memory, how they are joined, whole or a slice, and what one more costs.", + problem: + "The same card is sold whole, as a MIG slice, as a time-shared vGPU and as a peer's idle desktop, at prices an order of magnitude apart, and a listing that says one A100 has said almost nothing: 40 or 80 GB, PCIe or SXM, NVLinked or not. The peer markets move by the minute and publish their own schemas. A buyer's agent asked for two 80 GB cards with NVLink under 4 an hour reads six catalogs six ways.", + sample: `{ + "gpu": { + "model": "NVIDIA H100 SXM", + "vendor": "NVIDIA", + "count": 8, + "vram_mb": 81920, + "arch": "Hopper", + "interconnect": "nvlink", + "access": "passthrough", + "driver": "550", + "runtime": "CUDA 12.4", + "range": { + "key": "count", "min": 1, "max": 8, "step": 1, + "price": { "amount": 2.49, "currency": "USD", "interval": "hour", "per": 1 } + } + } +}`, + smallest: `{ "gpu": { "model": "NVIDIA RTX 4090" } }`, + fields: [ + ["model, vendor, arch", "the vendor's own name, required", "Including the form factor when the vendor distinguishes one: H100 SXM and H100 PCIe are two models."], + ["count, vram_mb", "integer; mebibytes per device", "80 GB is 81920. A reader multiplies for the total and never assumes the provider did."], + ["interconnect", "nvlink, nvswitch, infinity-fabric, pcie, none", "How the devices in one offer are joined. A single card states nothing."], + ["access", "passthrough, mig, vgpu, shared", "The whole device, a hardware partition (profile or fraction), a virtualised share, or time-sliced with neighbours."], + ["driver, runtime", "version strings", "What the provider installs by default; absent means the buyer installs their own."], + ["range", "key count, min, max, step, price", "One offer per model; the range is over how many."] + ], + directory: [ + ["Keep the model, match beside it", "NVIDIA H100 SXM and H100-SXM5-80GB are one card for search and two spellings on the page."], + ["Access beside VRAM", "A MIG slice and a whole card share a model and differ in everything else."], + ["Per-device price", "Computed from price and count so a row of eight and a row of one sort together, and labelled as computed."], + ["Stock with its timestamp", "GPU stock goes stale first; every row says when it was read."] + ], + absent: [ + ["No TFLOPS", "Vendor throughput depends on precision, sparsity and clock, and no two vendors quote it alike."], + ["No reservation calendar", "Whether a card is free next Tuesday is the provider's scheduler. stock says now."], + ["No spot flag", "A card at a spot price is a second offer; price.commitment and the url carry the terms."] + ] +}; + +export default function OpenGpuPage(): ReactNode { + return ; +} diff --git a/apps/logicsrc-web/src/app/openmemory/page.tsx b/apps/logicsrc-web/src/app/openmemory/page.tsx new file mode 100644 index 0000000..bee3c15 --- /dev/null +++ b/apps/logicsrc-web/src/app/openmemory/page.tsx @@ -0,0 +1,59 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { ResourceSpecPage, type ResourceSpec } from "@/components/resource-spec-page"; + +export const metadata: Metadata = { + title: "OpenMemory · LogicSRC", + description: + "OpenMemory is the memory block of an OpenServer offer: RAM in mebibytes, DDR generation and speed, ECC as three states, reserved or balloonable allocation, and a range that says how much more a buyer can add at checkout and for how much.", + alternates: { canonical: "/openmemory" } +}; + +const SPEC: ResourceSpec = { + name: "OpenMemory", + slug: "openmemory", + block: "memory", + tagline: + "The memory line of a server purchase, with the words fixed: how much, what kind, whether it is corrected, whether it is yours, and what a step more costs.", + problem: + "Memory is the resource most often bought in increments and least often described. A configurator offers 64, 128 or 256 GB at three prices and a scraper records the default. Eight gigabytes on a VPS may be reserved or may be reclaimed by the host under pressure. Whether the DIMMs are error-corrected decides whether a database belongs on the machine, and almost no listing says.", + sample: `{ + "memory": { + "ram_mb": 65536, + "type": "DDR5", + "ecc": true, + "speed_mts": 4800, + "channels": 8, + "allocation": "reserved", + "hugepages": true, + "range": { + "key": "ram_mb", "min": 32768, "max": 1048576, "step": 32768, + "price": { "amount": 12, "currency": "USD", "interval": "month", "per": 32768 } + } + } +}`, + smallest: `{ "memory": { "ram_mb": 8192 } }`, + fields: [ + ["ram_mb", "integer mebibytes, required", "What the guest sees. 8 GiB is 8192, the same unit OpenServer uses. Wins over compute.ram_mb when both are present."], + ["type, speed_mts, channels", "DDR4, DDR5, LPDDR5, HBM3; MT/s; integer", "The vendor's rating, stated not measured."], + ["ecc", "true, false, absent", "Three states. A directory never shows absent as false."], + ["allocation", "reserved, balloonable, shared", "Backed and never reclaimed, reclaimable under host pressure, or oversubscribed."], + ["swap_mb, hugepages", "integer, boolean", "Swap the provider configures by default; whether huge pages are allowed."], + ["range", "key, min, max, step, price", "Memory sold in steps: the example is 32 GiB steps at 12 USD a month each."] + ], + directory: [ + ["Mebibytes as the provider's unit", "Store ram_mb; display GiB or GB as the provider's page does, and say which."], + ["ECC as three states", "Yes, no and unstated are three filters."], + ["Price the range", "A configurator's three sizes are one offer with a range, shown as base plus step."], + ["memory before compute.ram_mb", "Read the block first and never sum the two."] + ], + absent: [ + ["No bandwidth or latency", "speed_mts is the DIMM rating. Measured memory bandwidth is a benchmark."], + ["No persistent-memory tier", "Persistent and CXL-attached memory sit under the provider's own key until there is a second provider to agree with."], + ["No per-process limits", "cgroup limits on a container platform are the platform's terms, linked from the offer's url."] + ] +}; + +export default function OpenMemoryPage(): ReactNode { + return ; +} diff --git a/apps/logicsrc-web/src/app/sitemap.ts b/apps/logicsrc-web/src/app/sitemap.ts index fa922cf..23009ee 100644 --- a/apps/logicsrc-web/src/app/sitemap.ts +++ b/apps/logicsrc-web/src/app/sitemap.ts @@ -26,6 +26,12 @@ 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: "/opencpu", changeFrequency: "weekly", priority: 0.9 }, + { path: "/openmemory", changeFrequency: "weekly", priority: 0.9 }, + { path: "/opengpu", changeFrequency: "weekly", priority: 0.9 }, + { path: "/openbandwidth", changeFrequency: "weekly", priority: 0.9 }, + { path: "/openfile", changeFrequency: "weekly", priority: 0.9 }, + { path: "/opendisk", 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 }, diff --git a/apps/logicsrc-web/src/components/resource-spec-page.tsx b/apps/logicsrc-web/src/components/resource-spec-page.tsx new file mode 100644 index 0000000..4d77fe1 --- /dev/null +++ b/apps/logicsrc-web/src/components/resource-spec-page.tsx @@ -0,0 +1,167 @@ +import Link from "next/link"; +import type { ReactNode } from "react"; +import { SiteShell } from "@/components/site-shell"; +import { mono, pre, table, td, th } from "../app/openontology/ui"; + +/** + * One landing page shape for the five OpenServer resource specifications + * (OpenCPU, OpenMemory, OpenDisk, OpenGPU, OpenBandwidth). Each page is + * the same five bands with different words, so the words live in the page + * and the bands live here. + */ + +export type ResourceSpec = { + /** Display name, e.g. "OpenCPU". */ + name: string; + /** URL slug under /docs and /, e.g. "opencpu". */ + slug: string; + /** The block key on an OpenServer offer, e.g. "compute". */ + block: string; + /** One line under the title. */ + tagline: string; + /** Two or three sentences on why the resource needs its own words. */ + problem: ReactNode; + /** The example block, pretty-printed JSON. */ + sample: string; + /** The smallest valid block, one line of JSON. */ + smallest: string; + /** Field name, values, meaning. */ + fields: Array<[string, string, string]>; + /** What a directory does with the block. */ + directory: Array<[string, string]>; + /** What is deliberately absent, and why. */ + absent: Array<[string, string]>; +}; + +const SIBLINGS: Array<[string, string]> = [ + ["OpenCPU", "opencpu"], + ["OpenMemory", "openmemory"], + ["OpenDisk", "opendisk"], + ["OpenGPU", "opengpu"], + ["OpenBandwidth", "openbandwidth"] +]; + +export function ResourceSpecPage({ spec }: { spec: ResourceSpec }): ReactNode { + const wellKnown = `/.well-known/${spec.slug}.json`; + return ( + +
+
+

LogicSRC standards surface · OpenServer resource

+

{spec.name}

+

{spec.tagline}

+
+

{spec.problem}

+

+ Status: 0.1. The {spec.block} block of an{" "} + OpenServer offer, written down on its own. A provider + that sells only this resource lists it as an offer and may serve the same document at{" "} + {wellKnown}. Every rule degrades: the smallest valid block is{" "} + {spec.smallest}. +

+
+ +
+
+

The {spec.block} block

+

+ Every key is the provider's claim, in fixed units. range{" "} + is the part a buyer can dial at checkout: the field, its bounds, the step and what a + step costs. +

+
+
{spec.sample}
+
+ +
+
+

Fields

+

Absent means unstated, never a default. A reader says so beside the number.

+
+ + + + + + + + + + {spec.fields.map(([key, values, meaning]) => ( + + + + + + ))} + +
KeyValuesMeaning
+ {key} + {values}{meaning}
+
+ +
+
+

What a directory does with it

+
+ + + {spec.directory.map(([what, how]) => ( + + + + + ))} + +
+ {what} + {how}
+
+ +
+
+

What is deliberately absent

+
+ + + {spec.absent.map(([what, why]) => ( + + + + + ))} + +
+ {what} + {why}
+
+ +
+
+

Where everything lives

+
+
    +
  • + Specification: the block, every rule and how + it degrades, the resource as its own offer, what a directory owes a provider +
  • +
  • + OpenServer: the descriptor at{" "} + /.well-known/openserver.json that this block sits in, and the + directory that reads it +
  • +
  • + The five resources of a purchase:{" "} + {SIBLINGS.map(([name, slug], i) => ( + + {i > 0 ? ", " : ""} + {slug === spec.slug ? {name} : {name}} + + ))} + , each with the same range shape +
  • +
+
+
+ ); +} diff --git a/apps/logicsrc-web/src/components/site-shell.tsx b/apps/logicsrc-web/src/components/site-shell.tsx index 5d82627..12a284b 100644 --- a/apps/logicsrc-web/src/components/site-shell.tsx +++ b/apps/logicsrc-web/src/components/site-shell.tsx @@ -18,6 +18,12 @@ const NAV: Array<{ href: string; label: string; external?: boolean }> = [ { href: "/openmcp", label: "OpenMCP" }, { href: "/openaccess", label: "OpenAccess" }, { href: "/openserver", label: "OpenServer" }, + { href: "/opencpu", label: "OpenCPU" }, + { href: "/openmemory", label: "OpenMemory" }, + { href: "/opengpu", label: "OpenGPU" }, + { href: "/openbandwidth", label: "OpenBandwidth" }, + { href: "/openfile", label: "OpenFile" }, + { href: "/opendisk", label: "OpenDisk" }, { href: "/opencoupon", label: "OpenCoupon" }, { href: "/openrecipe", label: "OpenRecipe.md" }, { href: "/#cli", label: "CLI" }, diff --git a/apps/logicsrc-web/src/lib/docs.ts b/apps/logicsrc-web/src/lib/docs.ts index fa8c725..2c74a13 100644 --- a/apps/logicsrc-web/src/lib/docs.ts +++ b/apps/logicsrc-web/src/lib/docs.ts @@ -21,9 +21,15 @@ export const DOC_SLUGS = [ "openmcp", "openaccess", "openserver", + "openfile", + "opendisk", "opencoupon", "openrecipe", "openstream", + "opencpu", + "openmemory", + "opengpu", + "openbandwidth", "openspec-comparison", "data-model", "cli", diff --git a/docs/openbandwidth.md b/docs/openbandwidth.md new file mode 100644 index 0000000..b0755f0 --- /dev/null +++ b/docs/openbandwidth.md @@ -0,0 +1,124 @@ +# OpenBandwidth + +OpenBandwidth is the shape of one resource in a server purchase: the network. It says how fast the port is, how much traffic is included and how it is metered, what overage costs, how many addresses come with the box and what more of them cost, and whether traffic is scrubbed. It is the `network` block of an [OpenServer](/docs/openserver) offer, written down on its own so a VPS host with a transfer cap, a colocation facility billing at the 95th percentile, an IPv4 lessor and a directory that filters on any of them all mean the same thing by the same key. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface. + +Status: **0.1**. One of five resource specifications under OpenServer: [OpenCPU](/docs/opencpu), [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu) and OpenBandwidth. Each describes one thing that is negotiable when a server is bought. + +Slug: `openbandwidth` + +## The problem + +"Unmetered 1 Gbps" and "1 Gbps, 20 TB" and "1 Gbps at 95th percentile, 100 Mbps committed" are three different products that every comparison site shows as 1 Gbps. The surprise on the invoice is always in the network line: overage at 0.01 a gigabyte on one host and 0.09 on another, ingress free here and billed there, a second IPv4 address at 2 a month or unavailable at any price. IPv4 addresses are now a market of their own, leased by the /24, and no catalog format has a place for them. A buyer's agent asked for "10 TB a month, egress under 0.02 a gigabyte over, with a /29" cannot answer from the number 1000. + +This document fixes the words for the port, the meter, the overage and the addresses. + +## Terms + +- The **network block** is the `network` object on an OpenServer offer, or the same object served on its own. +- The **port** is the link speed the buyer is connected at. The **meter** is how traffic on it is counted for billing. **Overage** is the price of traffic past what is included. +- **Addresses** are the IPv4 and IPv6 assignments included, and the price of more. +- A **range** is what the buyer may change at checkout, with the price of changing it. + +## The network block + +```json +{ + "network": { + "bandwidth_mbps": 1000, + "metering": "transfer", + "transfer_gb": 20000, + "counts": "egress", + "overage": { "amount": 0.01, "currency": "USD", "per_gb": 1 }, + "ipv4": 1, + "ipv4_price": { "amount": 2, "currency": "USD", "interval": "month", "per": 1 }, + "ipv4_max": 8, + "ipv6": "/64", + "ddos": "always-on", + "private_network": true, + "uplinks": 2, + "range": { + "key": "bandwidth_mbps", + "min": 1000, + "max": 10000, + "step": 1000, + "price": { "amount": 15, "currency": "USD", "interval": "month", "per": 1000 } + } + } +} +``` + +The smallest valid block states the port: + +```json +{ "network": { "bandwidth_mbps": 1000 } } +``` + +The rules, and every one degrades: + +1. **`bandwidth_mbps` is required.** It is the port speed in megabits per second, the same unit OpenServer uses. A shaped link states the shaped rate, not the physical port. +2. **`metering`** is one of `transfer`, `unmetered`, `percentile`, `flat`. `transfer` counts gigabytes per interval against `transfer_gb`. `unmetered` has no cap and `transfer_gb` is absent. `percentile` bills on the 95th percentile of utilisation, and `commit_mbps` is the committed rate included in the price. `flat` is a fixed price for the port, whatever passes. Absent with `transfer_gb` present means `transfer`; absent otherwise means unstated. +3. **`transfer_gb`** is included traffic per the offer's price interval, in gigabytes. **`counts`** says which direction is counted: `egress`, `ingress`, `both`, or `max` for the larger of the two. Absent means unstated, and a directory does not assume egress. +4. **`overage`** is the price of traffic past `transfer_gb`, as an `amount` in `currency` per `per_gb` gigabytes. Absent with a cap means the provider throttles or stops rather than bills, and `over_cap` may say which: `bill`, `throttle`, `suspend`. +5. **`ipv4`** is how many IPv4 addresses are included, an integer, as OpenServer states it. **`ipv4_price`** is the price of each additional address at the offer's interval, and **`ipv4_max`** the most the provider will assign. `0` included with a price means addresses are sold separately. **`ipv6`** is a boolean, as OpenServer states it, or the prefix length assigned as a string (`/64`, `/56`), which a reader treats as true. +6. **`ddos`** is `always-on`, `on-demand`, `none`, or absent for unstated. It states that mitigation exists, not what it withstands; the provider's terms say that. **`private_network`** is whether the offer has a private LAN to other servers of the same buyer. **`uplinks`** is the count of physical links on a dedicated box. +7. **`range`** is the negotiable part. `key` is `bandwidth_mbps`, `transfer_gb` or `ipv4`, `min`, `max` and `step` bound it, and `price` is the cost per `per` units at the offer's interval, on top of the base price. A block may carry more than one range as a list under `ranges` when two fields are negotiable; `range` alone is the common case. +8. **Unknown keys are kept.** A provider may say more; a reader passes it through under the provider's key. + +## Network as its own offer + +Bandwidth and addresses are sold without a server more often than any other resource: IP transit at the 95th percentile, a CDN's egress by the gigabyte, a leased IPv4 block, a cross-connect in a colocation facility. Each is an OpenServer offer with a `network` block and a `price`: + +```json +{ + "id": "transit-1g", + "name": "IP transit 1 Gbps", + "kind": "colocation", + "network": { "bandwidth_mbps": 1000, "metering": "percentile", "commit_mbps": 100, "overage": { "amount": 0.35, "currency": "USD", "per_mbps": 1 } }, + "price": { "amount": 35, "currency": "USD", "interval": "month" } +} +``` + +```json +{ + "id": "ipv4-24", + "name": "IPv4 /24 lease", + "kind": "colocation", + "network": { "bandwidth_mbps": 0, "ipv4": 256, "ipv4_prefix": "/24" }, + "price": { "amount": 130, "currency": "USD", "interval": "month", "commitment": "12 months" } +} +``` + +A percentile overage is priced per megabit, so `overage` carries `per_mbps` in place of `per_gb`. An address lease states `bandwidth_mbps: 0`, because the rule wants a port and there is none, and `ipv4_prefix` says the block size. A provider whose file is only network offers may serve it at `/.well-known/openbandwidth.json`; the shape is OpenServer's and the name says what is in it. A reader that only wants network filters offers on the presence of a `network` block. + +## What a directory does with it + +1. **Shows the meter beside the port.** 1 Gbps unmetered and 1 Gbps with 20 TB are two rows with two words, not one number. +2. **Prices a month of traffic.** For a `transfer` offer a directory can show what a stated volume would cost, base plus overage, and says which volume it assumed. +3. **Lists addresses as a resource.** Included count, price of more, ceiling. A row with `ipv4: 0` and no price is marked unstated, not free. +4. **Never assumes direction.** A cap with no `counts` is shown as a cap, with the direction unstated. + +## What is deliberately absent + +**No speed test.** `bandwidth_mbps` is the port the provider sells. Achieved throughput to any destination is a measurement and another document's business. + +**No carrier list or peering.** Which transit providers and exchanges sit behind the port is the provider's network page, linked from the offer's `url`. A provider that wants to state ASN or upstreams does so under its own key. + +**No latency or geography beyond OpenServer's `location`.** Where the box is comes from the offer. Round-trip times to anywhere are a measurement. + +**No SLA.** Uptime percentages and credits are the provider's terms. + +## Related standards + +- [OpenServer](/docs/openserver): the descriptor and the offer this block sits in. +- [OpenCPU](/docs/opencpu), [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu): the other four resources of a purchase, each with the same `range` shape. +- [OpenStream](/docs/openstream): the relay envelope a byte stream crosses a network in; unrelated to how the network is sold. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the network block, four meters, overage, addresses as a priced resource, the range. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/opencpu.md b/docs/opencpu.md new file mode 100644 index 0000000..53589ab --- /dev/null +++ b/docs/opencpu.md @@ -0,0 +1,111 @@ +# OpenCPU + +OpenCPU is the shape of one resource in a server purchase: the processor. It says how many cores or threads an offer has, what they are, whether they are yours alone or shared, and how many more a buyer may add at checkout and for how much. It is the `compute` block of an [OpenServer](/docs/openserver) offer, written down on its own so a provider that sells only compute, a configurator that sells it by the core and a directory that filters on it all mean the same thing by the same key. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface. + +Status: **0.1**. One of five resource specifications under OpenServer: OpenCPU, [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu) and [OpenBandwidth](/docs/openbandwidth). Each describes one thing that is negotiable when a server is bought. + +Slug: `opencpu` + +## The problem + +"4 vCPU" on one pricing page is four hyperthreads of a shared socket that will be throttled at 20% sustained load. On the next page it is four dedicated cores with the boost clock quoted. A comparison site collapses both into a column called CPU and sorts on the number, and the buyer who wanted the second pays for the first. A dedicated-server configurator lets a buyer choose between three processors and the price changes, but that choice lives in a form and nowhere a reader can see it. A buyer's agent asked for "8 dedicated cores, x86, under 40 a month" cannot answer from the number 8. + +The processor is one line of a spec sheet, but it is the line with the most ways to say the same thing, so this document fixes the words. + +## Terms + +- The **compute block** is the `compute` object on an OpenServer offer, or the same object served on its own. +- A **thread** is what a hypervisor hands a guest; a **core** is what the silicon has. Providers sell either, and this document keeps them apart. +- An **allocation** is whether the threads sold are reserved for the buyer, shared with neighbours, or shared with a credit balance that allows bursts. +- A **range** is what the buyer may change at checkout, with the price of changing it. + +## The compute block + +```json +{ + "compute": { + "vcpu": 4, + "cores": 2, + "threads_per_core": 2, + "arch": "x86_64", + "model": "AMD EPYC 9354", + "vendor": "AMD", + "base_ghz": 3.25, + "boost_ghz": 3.75, + "sockets": 1, + "allocation": "dedicated", + "range": { + "key": "vcpu", + "min": 2, + "max": 32, + "step": 2, + "price": { "amount": 4, "currency": "USD", "interval": "month", "per": 1 } + } + } +} +``` + +The smallest valid block states one count: + +```json +{ "compute": { "vcpu": 4 } } +``` + +The rules, and every one degrades: + +1. **One of `vcpu` or `cores` is required.** `vcpu` is threads sold to the guest; `cores` is physical cores. A virtual offer states `vcpu`, a dedicated box states `cores`, and one may state both, with `threads_per_core` (1 or 2) saying how they relate. A reader never derives one from the other unless `threads_per_core` is stated. +2. **`arch`** is `x86_64`, `arm64`, `riscv64` or the provider's own word. Absent means unstated, not x86. +3. **`model`** is the processor as the vendor names it, unchanged, so a buyer can look it up. `vendor` is the maker (`AMD`, `Intel`, `Ampere`, `Apple`, `AWS` for Graviton). `base_ghz` and `boost_ghz` are the vendor's clock figures in gigahertz, not a measurement. `sockets` is how many packages a dedicated box has. +4. **`allocation`** is one of `dedicated`, `shared`, `burstable`. `dedicated` means the threads are reserved for this buyer. `shared` means they are oversubscribed with neighbours and sustained use may be limited. `burstable` means shared with a credit balance: `credits_per_hour` says how many CPU credits accrue and `baseline_pct` the sustained share the buyer is entitled to without spending them. Absent means unstated, and a directory that sorts by core count says so beside the number. +5. **`range`** is the negotiable part. `key` names the field the buyer changes (`vcpu` or `cores`), `min`, `max` and `step` bound it, and `price` is the cost per `per` units at the offer's interval, on top of the offer's base price. An offer with no `range` is sold as stated. A configurator with a choice of processors lists one offer per processor rather than a range over `model`, because a model is a name, not a number. +6. **`ram_mb` may sit here for compatibility** with OpenServer 0.1, and a reader accepts it. The memory block in [OpenMemory](/docs/openmemory) is where memory belongs; when both are present the memory block wins. +7. **Unknown keys are kept.** A provider may say more; a reader passes it through under the provider's key. + +Units are fixed: counts are integers, clocks are gigahertz as decimals, percentages are integers 0 to 100. + +## Compute as its own offer + +A provider that sells compute without a server, a batch platform billing by the vCPU-hour, a peer on a marketplace renting its idle cores, a configurator selling core upgrades, lists it as an OpenServer offer whose `compute` block carries the goods and whose `price` says what a unit costs: + +```json +{ + "id": "batch-vcpu-hour", + "name": "Batch vCPU", + "kind": "serverless", + "compute": { "vcpu": 1, "arch": "x86_64", "allocation": "dedicated" }, + "price": { "amount": 0.021, "currency": "USD", "interval": "hour" } +} +``` + +A provider that sells only compute may serve its OpenServer descriptor at `/.well-known/opencpu.json` as well as, or instead of, `/.well-known/openserver.json`. The document is the same shape; the name says what a reader will find in it. A reader that only wants compute filters offers on the presence of a `compute` block. + +## What a directory does with it + +1. **Sorts on what was stated.** `vcpu` and `cores` are two columns, not one. A row with neither is listed and marked unstated. +2. **Shows allocation beside the count.** Four dedicated threads and four shared ones are different products at the same number, and the directory shows the word. +3. **Prices the range.** An offer with a `range` is shown at its base price with the per-unit price alongside, so a buyer can see that 8 vCPU costs the base plus four steps. +4. **Keeps the model string.** Normalise for search; display the vendor's name. + +## What is deliberately absent + +**No benchmarks.** Clock figures are the vendor's; a measured score is another document's business, and a directory that publishes one labels it as its own. + +**No instruction-set flags.** AVX-512, SVE, virtualisation extensions: a buyer who needs one looks up `model`. A provider that wants to state them does so under its own key. + +**No scheduling guarantees.** `allocation` says reserved, shared or burstable. Latency, NUMA placement and pinning are the provider's terms, linked from the offer's `url`. + +## Related standards + +- [OpenServer](/docs/openserver): the descriptor and the offer this block sits in. +- [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu), [OpenBandwidth](/docs/openbandwidth): the other four resources of a purchase, each with the same `range` shape. +- [OpenSwarm](/openswarm) and c0mpute: a peer renting its cores lists them with this block and settles under OpenSwarm. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the compute block, threads against cores, three allocations, the range. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/opendisk.md b/docs/opendisk.md new file mode 100644 index 0000000..656cb5e --- /dev/null +++ b/docs/opendisk.md @@ -0,0 +1,184 @@ +# OpenDisk + +OpenDisk is one file a machine serves about the disk it will rent: how much is free, what a GiB-month costs, where the box is, what it will hold, how it proves it is still holding it, and where it is paid. A requester reads the disk's own file instead of a marketplace's listing, a directory lists every disk that serves one, and a peer with a spare terabyte is on the market by putting a file at a URL. It is the web-facing door onto an [OpenSwarm](/openswarm) `paid2seed` seeder, and it is what a peer-to-peer storage market is made of. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface, with [d1sks.com](https://d1sks.com) as the reference marketplace. + +Status: **0.1**. A description of a file a marketplace will read, published so a peer can serve one and any directory can read it. + +Slug: `opendisk` + +## The problem + +OpenSwarm already says how a seeder is paid to hold a swarm: a requester posts a `pay2seed` offer at a hub with money escrowed, a seeder takes a `paid2seed` lease, proves it holds the pieces every period, and is paid per GiB-month. What it does not say is how a requester finds a seeder before posting, or how a seeder is found at all. The market in `paid2seed` is one-sided: offers are listed and seeders poll them. A requester who wants a box in Germany with 2 TB free and a year of clean proofs has no file to read, and a seeder with those things has no file to serve. + +Every other rentable thing has a listing. `/.well-known/` is where a host says things about itself. What is missing is the one file that puts a seeder's capacity, price and record where a requester can fetch it, in a shape every reader agrees on, so a disk can be found instead of waited for. + +## Terms + +- A **disk** is capacity somebody will rent: a peer's spare drive, a seedbox, a storage node, a provider's storage plan. Its **descriptor** is the file it serves about itself. +- The **seeder** is the OpenSwarm identity that takes leases for the disk and is paid. One disk, one seeder key. +- A **requester** is whoever wants bytes kept, as `pay2seed` defines it: a publisher, a person with a backup, an agent. +- A **marketplace** is a directory of disks that also fronts a hub, so a requester can read a disk and post the offer in one place. [d1sks.com](https://d1sks.com) is the reference. +- A **reader** is anything that reads a descriptor: a marketplace, a requester's client, a person's terminal, an agent. + +## The descriptor + +A disk serves a JSON document at `/.well-known/opendisk.json` on its own origin. + +```json +{ + "name": "seeder-a41e, Falkenstein", + "web": "https://seeder-a41e.example", + "operator": "https://seeder-a41e.example/.well-known/openprofile.md", + "key": "ed25519:a41e7f0c2d9b8e5a6f3c1d4e7b0a9f8c5d2e1b4a7c0f3e6d9b2a5c8e1f4d7b0a", + "hubs": ["https://bittorrented.com/api/openswarm", "https://d1sks.com/api/openswarm"], + "updated": "2026-09-13T06:00:00Z", + "capacity": { "total_gib": 3726, "free_gib": 2210, "reserved_gib": 200 }, + "price": { + "currency": "USD", + "per_gib_month": 0.12, + "per_gib_transfer": 0.004, + "min_gib": 1, + "max_gib": 2000, + "min_days": 7, + "max_days": 365 + }, + "location": { "regions": ["fsn1"], "countries": ["DE"] }, + "network": { "bandwidth_mbps": 1000, "transfer_gb": 20000, "ipv4": 1, "ipv6": true }, + "storage": [{ "type": "hdd", "size_gb": 4000 }], + "accepts": { + "visibility": ["private", "public"], + "bases": ["own", "licensed", "open-license", "public-domain", "personal"], + "encrypted_only": false, + "max_file_gib": 500, + "feeds": true + }, + "proof": { "kinds": ["challenge", "probe"], "every_hours_min": 6, "webhook": "https://seeder-a41e.example/openswarm/hooks" }, + "record": { + "source": "https://bittorrented.com/api/openswarm/pay2seed/seeders/ed25519:a41e7f0c2d9b8e5a6f3c1d4e7b0a9f8c5d2e1b4a7c0f3e6d9b2a5c8e1f4d7b0a", + "standing": 412, + "proven": 418, + "failed": 3, + "abandoned": 0, + "since": "2025-11-02T00:00:00Z" + }, + "payout": { "payee": "ed25519:a41e7f0c2d9b8e5a6f3c1d4e7b0a9f8c5d2e1b4a7c0f3e6d9b2a5c8e1f4d7b0a", "network": "eip155:8453" }, + "holding": "https://seeder-a41e.example/openswarm/holding" +} +``` + +The smallest valid descriptor is a name, the free space and a price: + +```json +{ "name": "spare drive, Lisbon", "capacity": { "free_gib": 900 }, "price": { "currency": "USD", "per_gib_month": 0.08 } } +``` + +The rules, and every one degrades: + +1. **`name`, `capacity.free_gib` and `price.per_gib_month` are the only required keys.** A descriptor with those alone is valid: it is a disk, it has room, it has a price. A reader lists what it was given and reports the rest as unstated rather than assumed. +2. **`operator`** is the person or organisation answerable, as an [OpenProfile.md](/openprofile) URL. `web` is the disk's site, if it has one beyond the descriptor. A disk with no operator is listed as such, and a marketplace may decline to list it. +3. **`key`** is the seeder's OpenSwarm identity, the key that signs `paid2seed` proofs and is registered as an `ippay` payee. **`hubs`** are the hubs it takes leases at. A requester posts its offer at one of them; a disk with no `hubs` is rented some other way, and `web` says how. +4. **`updated`** is when anything in the file last changed. `capacity.free_gib` changes with every lease, so a disk that is busy updates often and a reader with `updated` unchanged since its last fetch may skip the rest. +5. **`capacity`** is in GiB: `total_gib` the disk, `free_gib` what a new lease can have now, `reserved_gib` what the operator keeps back. `free_gib` is the number that matters and the only one required. +6. **`price`** is one price list. `per_gib_month` is what one GiB held for thirty days costs, pro rata, the same unit `pay2seed` offers are priced in. `per_gib_transfer` is what serving one GiB costs on top, or absent when serving is included; a disk that prices transfer in more detail does so as [OpenBandwidth](/docs/openbandwidth) describes and keeps this one number as the summary. `min_gib`, `max_gib`, `min_days`, `max_days` bound a lease. `currency` is ISO 4217. +7. **`location`, `network`, `storage`** are as [OpenServer](/openserver) defines them, the same keys and the same units, so a directory that already reads an OpenServer storage block reads this one: the provider's own region names and ISO country codes, megabits and gigabytes, one `storage` entry per drive with `type` `nvme`, `ssd` or `hdd`. A requester choosing a disk for a backup wants the country; one choosing a seed for a release wants the bandwidth. +8. **`accepts`** is the operator's policy, the same policy a `paid2seed` seeder client applies when it polls a market, stated up front so a requester does not post an offer this disk would never take. `visibility` and `bases` are `pay2seed`'s lists. `encrypted_only: true` means the disk holds `ipfile` ciphertext and nothing else. `max_file_gib` caps one swarm. `feeds` says whether it follows `ipdb` feeds and keeps their segments. +9. **`proof`** is how the disk expects to be checked: `kinds` any of `challenge` and `probe` as `paid2seed` §4 defines them, `every_hours_min` the shortest period it will accept, `webhook` where a hub pushes challenges so the disk need not poll. +10. **`record`** is the disk's history, and its `source` is the hub's own `GET /seeders/` for this seeder. The numbers are copied from there for a reader's convenience; the hub's answer wins, and a marketplace reads the hub rather than the file for anything it ranks on. +11. **`payout`** names the `ippay` payee and the network it is paid on. The address itself lives at the hub, registered by the seeder key, and this file never carries it. +12. **`holding`** is a URL that answers with what the disk holds right now, or the same list inline. The shape is below. +13. **Unknown keys are kept.** A disk says more than this document names, and a reader passes it through under the disk's own key. + +Serve it as `application/json`. The descriptor is a claim; that it came from the disk's own origin is one verification, and the hub's record under `key` is the other. + +## Holding + +The other half of a `holders` list in [OpenFile](/openfile): what one disk is holding, from the disk's side. + +```json +{ + "updated": "2026-09-13T06:14:02Z", + "holding": [ + { "id": "sha256:d6c3f828…c493ff", "swarm": "sha256:4b74eb43…c7a342", "lease": "sha256:5c02…", "gib": 0.68, "since": "2026-09-05T18:30:00Z", "until": "2026-10-05T18:00:00Z", "provenAt": "2026-09-13T06:00:05Z" }, + { "id": "sha256:1f9e…", "swarm": "sha256:88c0…", "lease": "sha256:0e4d…", "gib": 122.4, "since": "2026-08-01T00:00:00Z", "until": "2027-08-01T00:00:00Z", "provenAt": "2026-09-13T00:00:02Z" } + ] +} +``` + +`id` is the OpenFile content hash when the disk knows it, `swarm` the `infohashV2`, `lease` the `paid2seed` lease. A private swarm's `id` is its `plainRoot` from the manifest, which reveals nothing about the bytes; a disk holding `personal` swarms omits them from the public list, because a backup's existence is the requester's to announce. + +## Renting a disk + +A requester that has read a descriptor and wants the disk: + +1. Checks `accepts` against what it wants kept, and `record.source` at the hub for the disk's standing. +2. Makes its attestation, as `pay2seed` §3 requires, and posts a `pay2seed.offer` at one of the disk's `hubs` with `priceUsdPerGibMonth` at or above `price.per_gib_month` and `seeders.min` of one. +3. Waits for the disk's seeder client to take the lease, which it does on its next poll or at once if the hub pushes to `proof.webhook`. +4. Watches the lease as `pay2seed` §7 says: proven periods, spend against the budget, the next challenge due. + +A marketplace collapses those into one act: read the disk, post the offer, and show the lease when it lands. A requester that wants several disks posts one offer with `seeders.min` above one and lets the hub fill it, or reads several descriptors and posts to each. Nothing in the swarm side changes: leases, challenges, probes, receipts and payout are `paid2seed` exactly as written. + +## Marketplaces + +A marketplace reading descriptors: + +1. **Fetches on a schedule, hourly at least**, because `free_gib` moves. A disk that has told the marketplace its `updated` has not changed is skipped. +2. **Dedupes on `key`.** One seeder identity is one disk, whatever URL its descriptor was found at. A descriptor with no `key` dedupes on its origin. +3. **Reads standing from the hub**, never from the file, for anything it sorts or filters by. The file says where; the hub says how much. +4. **Lists a disk's policy beside its price**, so a requester sees that a cheap disk takes public swarms only, or ciphertext only, before it posts. +5. **Marks a disk gone, not deleted**, when its descriptor stops answering, and shows the leases it still holds until they end. +6. **Reports absence as absence.** An unstated operator is unstated. An unstated `accepts` means the disk will say when the offer arrives. + +[d1sks.com](https://d1sks.com) is the reference marketplace: a directory of every OpenDisk descriptor it has read, standing pulled from the hubs each disk names, and an offer form that posts to the disk's hub. [nichedb.dev](https://nichedb.dev) lists every disk in its hosting collection as an OpenServer offer, by the mapping below. + +## As an OpenServer offer + +A disk is a hosting offer, and a directory that reads [OpenServer](/openserver) descriptors lists it as one without a second parser: + +| OpenServer | from OpenDisk | +|---|---| +| `provider.name`, `provider.web`, `provider.operator`, `provider.country` | `name`, `web`, `operator`, `location.countries[0]` | +| `offers[].id` | `key`, or the descriptor's origin when there is no key | +| `offers[].name` | `name` | +| `offers[].url` | the descriptor's URL | +| `offers[].kind` | `storage` | +| `offers[].premises`, `management`, `tenancy` | `off-prem`, `unmanaged`, `shared` | +| `offers[].model` | `p2p` when the operator is a person or a peer, `centralized` when it is a provider; the disk may state `model` itself and that wins | +| `offers[].location`, `network`, `storage` | the same keys, unchanged | +| `offers[].price` | `{ "amount": price.per_gib_month, "currency": price.currency, "interval": "month", "unit": "gib" }`, `unit` kept as the provider's own key | +| `offers[].stock` | `in_stock` when `free_gib` is at least `price.min_gib`, else `out_of_stock` | +| `offers[].updated` | `updated` | + +A provider that sells storage plans and rents disk to swarms serves both files: an OpenServer descriptor for its plans and an OpenDisk descriptor for what a seeder can lease. A peer with one drive serves only this one, and is listed in both places. + +## What is deliberately absent + +**No lease in this file.** Leases, proofs, receipts and payout are `paid2seed`, and this document does not restate them. The descriptor is the ask; the offer is the bid; the lease is the match, and it is made at the hub. + +**No escrow at the disk.** Money sits at a hub, as `pay2seed` says. A disk never holds a requester's funds and never has to be trusted with them. + +**No reputation of its own.** `record` is a copy of what a hub signed, and a marketplace reads the hub. A disk that claims a standing its hub does not confirm is listed with the hub's number. + +**No plaintext.** A disk holds what `accepts` says, and by default that is `ipfile` ciphertext it cannot read. A `personal` swarm's existence is not in the public `holding` list. + +**No central registry.** Anyone may serve a descriptor and anyone may read it. d1sks.com is one marketplace among any number, and two marketplaces reading the same file list the same disk. + +## Serving one + +By hand, or by the seeder client. `torlnk serve` and a c0mpute node with `--openswarm` have every number this file needs: free space is the filesystem, the price and policy are the operator's configuration, the record is the hub's. Write it to the web root next to `robots.txt` and rewrite it when a lease starts or ends. + +## Related standards + +- [OpenSwarm](/openswarm): `pay2seed` for the offer and the attestation, `paid2seed` for leases, proofs, receipts and standing, `ippay` for the payee, `ipfile` for what is held. +- [OpenFile](/openfile): the publisher's side, and the `holders` list a disk appears in. +- [OpenServer](/openserver): the hosting offer a disk maps onto, and the units this document borrows. +- [OpenProfile.md](/openprofile): the `operator` behind a disk. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the descriptor, holding, renting a disk, marketplaces, the OpenServer mapping. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/openfile.md b/docs/openfile.md new file mode 100644 index 0000000..a9d8fe4 --- /dev/null +++ b/docs/openfile.md @@ -0,0 +1,192 @@ +# OpenFile + +OpenFile is one file a publisher serves about the files it has published: what each one is, how big, how to fetch it over a swarm or over plain HTTP, how to verify the bytes, on what basis it may be distributed, what it costs, and who is holding it right now. A directory reads the publisher's own file instead of crawling the DHT for bare infohashes, a browser with no torrent client still gets the bytes, and the publisher stays the author of its own listing. It is the web-facing door onto an [OpenSwarm](/openswarm) `ipfile` swarm, and it works for a plain HTTP download too. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface. + +Status: **0.1**. A description of a file a directory will read, published so a publisher can serve one and any reader can read it. + +Slug: `openfile` + +## The problem + +A file on a swarm is findable by its infohash and by nothing else. A DHT crawl sees forty million of them and can say what none of them are. OpenSwarm fixed the inside of the swarm: an `ipfile` manifest names the file, signs the price and the payout, and encrypts the pieces; `pay2seed` attaches consent and a README; `ipdb` replicates the catalogue between peers. What none of that gives a person with a browser, a search engine, or a directory is a URL to start from. The manifest lives on the DHT under a key, the catalogue is a feed found through the DHT, and a reader that speaks only HTTP is outside looking in. + +The pieces exist. `/.well-known/` is where a host says things about itself. Gateways already serve swarm bytes as webseeds. What is missing is the one file that puts a publisher's catalogue where an HTTP reader can fetch it, in a shape every reader agrees on, so a file can be found instead of crawled. + +## Terms + +- A **publisher** is whoever put a file up and signed for it: a person, an organisation, an agent. Its **descriptor** is the file it serves about its files. +- A **file** is one published thing: a document, a recording, a dataset, a release, a bundle. On a swarm it is one `ipfile` manifest; over HTTP it is one URL. +- A **holder** is anything that currently has the bytes and will serve them: a seeder with a lease, a gateway, a peer. +- A **directory** is anything that reads descriptors and lists files across publishers: a search index, a database, a media site, an agent's own cache. +- A **reader** is anything that reads a descriptor: a directory, a person's terminal, a program, an agent. + +## The descriptor + +A publisher serves a JSON document at `/.well-known/openfile.json` on its own origin. + +```json +{ + "publisher": { + "name": "Dartmoor Field Recordings", + "web": "https://dartmoor.example", + "operator": "https://dartmoor.example/.well-known/openprofile.md", + "key": "ed25519:5d292428e8a68946e5225136c8b10e8f33ab78a45e1663d0730996dd2b63d59e", + "feed": "ed25519:5d292428e8a68946e5225136c8b10e8f33ab78a45e1663d0730996dd2b63d59e/default", + "hubs": ["https://bittorrented.com/api/openswarm"] + }, + "updated": "2026-09-13T06:00:00Z", + "files": [ + { + "id": "sha256:d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff", + "name": "interview-2026-09-05.flac", + "url": "https://dartmoor.example/recordings/interview-2026-09-05", + "descriptor": "https://dartmoor.example/recordings/interview-2026-09-05.openfile.json", + "size": 734003200, + "contentType": "audio/flac", + "pieces": { "length": 1048576, "count": 700, "layer": "https://gw.c0mpute.com/swarm/4b74eb43677e4d03af5fb0856333f9aa21d9a5a3bbf944b13aa7eef379c7a342/layer" }, + "swarm": { + "file": "ed25519:0d87e09c7fea3ad6ba6c2f3e027ea47f5b245452899910948470906704c5295d", + "infohashV1": "sha1:a3ce2180413415d7cf4268fb892b8ffd539e8459", + "infohashV2": "sha256:4b74eb43677e4d03af5fb0856333f9aa21d9a5a3bbf944b13aa7eef379c7a342", + "manifest": "https://gw.c0mpute.com/swarm/4b74eb43677e4d03af5fb0856333f9aa21d9a5a3bbf944b13aa7eef379c7a342/manifest", + "trackers": ["wss://tracker.openwebtorrent.com", "udp://tracker.opentrackr.org:1337/announce"], + "private": false + }, + "encryption": "ipfile", + "fetch": [ + { "kind": "ipfile", "url": "magnet:?xs=urn:btpk:0d87e09c7fea3ad6ba6c2f3e027ea47f5b245452899910948470906704c5295d&s=ipfile" }, + { "kind": "webseed", "url": "https://gw.c0mpute.com/swarm/4b74eb43677e4d03af5fb0856333f9aa21d9a5a3bbf944b13aa7eef379c7a342/data" }, + { "kind": "http", "url": "https://gw.c0mpute.com/file/d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff" }, + { "kind": "hls", "url": "https://gw.c0mpute.com/file/d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff/index.m3u8" } + ], + "attestation": { + "record": "sha256:8e1c4a0b9d2f7e6c5b4a3d2e1f0c9b8a7d6e5f4c3b2a1d0e9f8c7b6a5d4e3f2c", + "basis": "own", + "license": "CC-BY-4.0", + "notice": "https://dartmoor.example/.well-known/pay2seed-notice" + }, + "readme": "https://dartmoor.example/recordings/interview-2026-09-05/README.md", + "price": { "amount": 0.5, "currency": "USD", "per": "key", "offer": "https://keys.dartmoor.example/openswarm/grant?file=0d87e09c" }, + "holders": "https://bittorrented.com/api/openswarm/files/d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff/holders", + "updated": "2026-09-13T06:00:00Z" + } + ] +} +``` + +The smallest valid descriptor is a publisher with a name and a file with a content hash and a name: + +```json +{ "publisher": { "name": "Dartmoor Field Recordings" }, "files": [{ "id": "sha256:d6c3f828…c493ff", "name": "interview-2026-09-05.flac" }] } +``` + +The rules, and every one degrades: + +1. **`publisher.name`, `files[].id` and `files[].name` are the only required keys.** A descriptor with those alone is valid. A reader lists what it was given and reports the rest as unstated rather than assumed. +2. **`files[].id` is the content hash of the plaintext**, `sha256:` and hex. For an `ipfile` swarm it is the manifest's `plainRoot`, so the id a reader gets here is the root the decrypted file verifies against. For a plain HTTP file it is the SHA-256 of the bytes. It is the dedupe key: two publishers serving the same bytes list the same id, and a directory that meets the same id twice has one file with two publishers, not two files. +3. **`publisher`** is who put it up. `web` is the site, `operator` the person or organisation answerable as an [OpenProfile.md](/openprofile) URL, `key` the OpenSwarm publisher key that signed the manifests, `feed` the `ipdb` feed a swarm reader can follow instead of polling this file, `hubs` the `ippay` hubs whose passes the publisher accepts. A publisher with no swarm has none of the last three. +4. **`updated`** on the descriptor is when anything in it last changed; **`updated`** on a file is when that file last changed and wins for that file. A reader with the descriptor's `updated` unchanged since its last fetch may skip the rest. +5. **`descriptor`** is the URL of the same file object served on its own, next to the file: `.openfile.json`, or wherever the publisher puts it. A reader handed a single file's descriptor by that route has everything below without the listing. +6. **`size`, `contentType`, `pieces`** describe the bytes. `size` is the plaintext length in bytes. `pieces.length` is the piece length, a power of two; `pieces.count` follows from it; `pieces.layer` is where to fetch the plaintext piece layer, so a reader can verify each piece as it arrives rather than the whole at the end. +7. **`swarm`** is how a torrent client reaches it: the `ipfile` file key, both infohashes, where the signed manifest can be fetched over HTTP, the trackers, and whether the swarm is private. Absent means there is no swarm and `fetch` is the whole story. +8. **`encryption`** is `ipfile` or `none`. Absent means `ipfile`: a file on a swarm is ciphertext by default, and a publisher that wants anyone to read the bytes off the wire says `none` as an explicit act. It says nothing about HTTP fetches, which a gateway serves decrypted to a pass holder. +9. **`fetch`** is the list of ways to get the bytes, each `{kind, url}`, in the publisher's order of preference. `kind` is `ipfile` (a magnet for an OpenSwarm client), `magnet` (a vanilla magnet), `webseed` (BEP 19 ciphertext by range), `http` (the plaintext by range from a gateway, behind a pass when there is a price), `hls` (a media file as a standard playlist from a gateway, sealed when there is a price). A reader picks the first kind it speaks. A browser with nothing installed speaks `http` and `hls`, which is the point. +10. **`attestation`** is the consent the file was published under, as `pay2seed` defines it: the record id, the `basis` (`own`, `licensed`, `open-license`, `public-domain`, `personal`), the SPDX `license` when the basis is a licence, and the `notice` endpoint. A file with no attestation is listed as unattested, and a directory that requires consent does not list it. +11. **`readme`** is the URL of the swarm's `README.md`, the one `pay2seed` requires at the root of every listed swarm. A directory renders it as the file's page. +12. **`price`** is what the bytes or the key cost, or absent for free. `amount` and `currency` as ISO 4217; `per` is `key` (one grant, the `ipfile` `keyUsd`), `gib` (per GiB served, the `ipfile` `perGib`), or `fetch` (a flat price for an HTTP download). `offer` is a URL that answers `402` with an x402 offer, exactly as `ippay` sells a pass, so a reader with a wallet and no hub account can pay where it stands. +13. **`holders`** is a URL that answers with who has the bytes right now, or the same list inline. The shape is below. +14. **Unknown keys are kept.** A publisher says more than this document names, and a reader passes it through under the publisher's own key. `ipaudio`, `ipvideo` and any later member of the family put their record here. + +Serve it as `application/json`. The descriptor is a claim; that it came from the publisher's own origin is one verification, and the manifest's signature by `publisher.key` is the other. + +## Holders + +A file is only as available as the machines holding it. `holders` answers the question a reader asks before it commits to a fetch: + +```json +{ + "id": "sha256:d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff", + "updated": "2026-09-13T06:14:02Z", + "holders": [ + { "kind": "seeder", "id": "ed25519:a41e…", "lease": "sha256:5c02…", "provenAt": "2026-09-13T06:00:05Z", "countries": ["DE"], "disk": "https://seeder-a41e.example/.well-known/opendisk.json" }, + { "kind": "gateway", "url": "https://gw.c0mpute.com", "seenAt": "2026-09-13T06:13:40Z", "countries": ["US"] }, + { "kind": "peer", "id": "ed25519:77aa…", "seenAt": "2026-09-13T05:58:11Z" } + ] +} +``` + +- **`kind`** is `seeder` (holds a `paid2seed` lease and passed its last proof), `gateway` (serves the file over HTTP), or `peer` (was seen in the swarm; nothing is promised). +- **`provenAt`** is when a seeder last answered a storage challenge or probe; **`seenAt`** is when a gateway or peer was last reached. A reader shows the age and treats a holder older than one proof period as unknown. +- **`lease`** is the `paid2seed` lease record, so a reader can check the seeder's standing at the hub rather than take the list's word. +- **`disk`** is the holder's own [OpenDisk](/opendisk) descriptor when it serves one, so a reader that likes a holder can rent more of it. + +The list is usually served by a hub, because the hub is what holds the leases and runs the proofs; `bittorrented.com` serves it for every swarm it lists. A publisher may inline a snapshot for a file it seeds itself. Either way the list is a claim about the moment it was made, and a reader that needs certainty fetches. + +## Discovery + +A reader finds a descriptor four ways, in this order: + +1. `/.well-known/openfile.json` on the publisher's origin. +2. `` in the HTML of a page about a file, or a `Link: <...>; rel="openfile"` header on the file itself or its page. +3. `.openfile.json` next to the file, for a directory listing or a static site. +4. A URL handed to the reader directly. + +A descriptor is **verified** when it was fetched from the same origin as `publisher.web`, or from `/.well-known/` on the origin the reader was pointed at. A file inside it is verified a second way when its `swarm.manifest` fetches, its first signature is by `swarm.file`, its second is by `publisher.key`, and its `plainRoot` equals `id`. A directory shows both facts. One found by the fourth route on some other host is a claim about the publisher by whoever hosts it, and a directory marks it so. + +## Fetching and verifying + +A conforming reader that wants the bytes: + +1. Picks the first `fetch` entry whose `kind` it speaks. +2. Pays if there is a `price`: a swarm client buys a pass at one of `publisher.hubs`; an HTTP client requests `price.offer`, gets a `402` and an x402 offer, pays, and presents the pass as `Authorization: Bearer` on the gateway, as `ippay` §7 defines. +3. Fetches. Over `ipfile` it verifies each piece against the plaintext piece layer as `ipfile` §5.6 says. Over `http` it fetches the layer from `pieces.layer` and verifies each piece the same way, or hashes the whole and compares with `id` at the end when there is no layer. +4. Refuses bytes that fail either check, and says which holder served them. + +Nothing in this is new. It is `ipfile` §10 written for a client that started from a URL instead of a key. + +## Directories + +A directory reading descriptors: + +1. **Fetches on a schedule, daily at least**, and follows `publisher.feed` on the swarm when it can, because the feed is the same catalogue with a head a DHT `get` finds. +2. **Dedupes on `id`.** The same content hash from two publishers is one file with two listings, and a reader sees both names and both attestations. +3. **Keeps the publisher's words.** The `name`, the README, the extra keys. A directory normalises for search and displays what the publisher wrote. +4. **Lists nothing it could not verify from the origin or the manifest**, and marks which of the two it has. +5. **Shows the basis** beside every file it lists, as `paid2seed` §6.2 makes a seeder client show it. A reader knows whether it is looking at somebody's own work, an open licence, or a claim. +6. **Reports absence as absence.** An unstated price is free; an unstated attestation is unattested, not consented; an unstated holder list is unknown, not empty. + +The first directory reading OpenFile is `bittorrented.com`, which today lists bare infohashes from a DHT crawl and will list consented swarms beside them with their README as the page. A file's `holders` there come from its own leases and probes. [nichedb.dev](https://nichedb.dev) lists holders that serve an OpenDisk descriptor in its hosting collection. + +## What is deliberately absent + +**No new swarm format.** The swarm is `ipfile`, the manifest is `ipfile`'s, the consent is `pay2seed`'s, the payment is `ippay`'s. This document is a JSON door onto records that already exist, so a publisher that already serves a swarm writes the file from what it has. + +**No search.** A descriptor lists one publisher's files. Finding a file across publishers is a directory's job, and two directories reading the same descriptors list the same files. + +**No trust score.** `verified` is a fact about where the file came from and who signed the manifest. `basis` is what the publisher claimed. Whether either is true is the reader's judgement, with the operator's profile and the notice endpoint as the place to start. + +**No DRM.** A pass holder gets the key and the bytes, as `ipfile` says. A publisher that wants to control a device after delivery is reading the wrong specification. + +**No product domain yet.** A directory and a marketplace for OpenFile is planned under a name not yet chosen. `bittorrented.com` is the reference reader until then. + +## Serving one + +By hand, from the manifests a publisher already signed. `ip file add` writes the swarm; the descriptor is the same fields, exported, at a fixed URL. A static site commits `openfile.json` next to `robots.txt` and one `.openfile.json` beside each file. + +## Related standards + +- [OpenSwarm](/openswarm): `ipfile` for the swarm and manifest, `pay2seed` for the attestation and the README, `paid2seed` for leases and proofs, `ippay` for passes and the x402 offer, `ipdb` for the feed. +- [OpenDisk](/opendisk): what a holder serves about the disk it rents, and how a publisher buys more holders. +- [OpenServer](/openserver): how a gateway or a seeder is listed as a hosting offer. +- [OpenProfile.md](/openprofile): the `operator` behind a publisher. +- [OpenMCP](/openmcp): a directory that also serves its rows over MCP describes that door with an OpenMCP descriptor. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the descriptor, the per-file descriptor, holders, discovery, fetching and verifying, what a directory owes a publisher. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/opengpu.md b/docs/opengpu.md new file mode 100644 index 0000000..c9199b1 --- /dev/null +++ b/docs/opengpu.md @@ -0,0 +1,113 @@ +# OpenGPU + +OpenGPU is the shape of one resource in a server purchase: the accelerator. It says which GPU an offer has, how many, how much memory each carries, how they are joined, whether the buyer gets the whole card or a slice of it, and how many more a buyer may add at checkout and for how much. It is the `gpu` block of an [OpenServer](/docs/openserver) offer, written down on its own so a cloud selling H100 hours, a peer renting a gaming card and a directory that filters on VRAM all mean the same thing by the same key. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface. + +Status: **0.1**. One of five resource specifications under OpenServer: [OpenCPU](/docs/opencpu), [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), OpenGPU and [OpenBandwidth](/docs/openbandwidth). Each describes one thing that is negotiable when a server is bought. + +Slug: `opengpu` + +## The problem + +GPU pricing is the most volatile and least comparable line in hosting. The same card is sold whole, as a MIG slice, as a time-shared vGPU and as a peer's idle desktop, at prices an order of magnitude apart, and a listing that says "1x A100" has said almost nothing: 40 GB or 80 GB, PCIe or SXM, NVLinked to its neighbours or not. The peer-to-peer markets move by the minute and publish their own schemas. A buyer's agent asked for "two 80 GB cards with NVLink, under 4 an hour, in stock" reads six catalogs six ways. + +This document fixes the words, and it fixes them so a marketplace listing and a hyperscaler SKU can sit in one column. + +## Terms + +- The **gpu block** is the `gpu` object on an OpenServer offer, inside `compute` as OpenServer 0.1 places it, or beside it. +- **Access** is how the buyer reaches the silicon: the whole device, a hardware partition, a virtualised share, or a time-shared queue. +- An **interconnect** is how the cards in one offer talk to each other. +- A **range** is what the buyer may change at checkout, with the price of changing it. + +## The gpu block + +```json +{ + "gpu": { + "model": "NVIDIA H100 SXM", + "vendor": "NVIDIA", + "count": 8, + "vram_mb": 81920, + "arch": "Hopper", + "interconnect": "nvlink", + "access": "passthrough", + "fraction": 1, + "driver": "550", + "runtime": "CUDA 12.4", + "range": { + "key": "count", + "min": 1, + "max": 8, + "step": 1, + "price": { "amount": 2.49, "currency": "USD", "interval": "hour", "per": 1 } + } + } +} +``` + +The smallest valid block states the model: + +```json +{ "gpu": { "model": "NVIDIA RTX 4090" } } +``` + +The rules, and every one degrades: + +1. **`model` is required.** It is the card as the vendor names it, unchanged, including the form factor when the vendor distinguishes one (`NVIDIA H100 SXM`, `NVIDIA H100 PCIe`, `AMD Instinct MI300X`). `vendor` is the maker. `arch` is the vendor's architecture name. +2. **`count`** is how many devices the offer includes; absent means 1. **`vram_mb`** is the memory of one device in mebibytes, the same unit OpenServer uses. 80 GB is 81920. A reader multiplies by `count` for the total and never assumes the provider did. +3. **`interconnect`** is how the devices in the offer are joined: `nvlink`, `nvswitch`, `infinity-fabric`, `pcie`, or `none` when they are independent cards. It describes the offer, so a single card states nothing here. +4. **`access`** is one of `passthrough`, `mig`, `vgpu`, `shared`. `passthrough` is the whole device. `mig` is a hardware partition and `fraction` or `profile` says which: `profile` is the vendor's name (`1g.10gb`), `fraction` the share as a decimal (`0.125`). `vgpu` is a virtualised share with `fraction`. `shared` is time-sliced with neighbours and no fixed share. Absent means unstated, and a directory that sorts by VRAM says so beside the number. +5. **`driver`** and **`runtime`** are what the provider installs by default, as version strings; absent means the buyer installs their own. A bare-metal offer usually states nothing here. +6. **`range`** is the negotiable part. `key` is `count`, `min`, `max` and `step` bound it, and `price` is the cost per `per` devices at the offer's interval, on top of the base price. A provider that offers several cards lists one offer per `model`, because a model is a name, not a number. +7. **Position.** OpenServer 0.1 places `gpu` inside `compute`. A provider may also place it at the top level of the offer; a reader looks in both places and a block at the top level wins. +8. **Unknown keys are kept.** A provider may say more; a reader passes it through under the provider's key. + +## GPU as its own offer + +GPU is already an OpenServer `kind`, because the accelerator is what is sold and the host beside it is incidental. An offer of `kind: gpu` states the block and a price, and the `compute` and `memory` around it describe the host: + +```json +{ + "id": "h100-1", + "name": "H100 80GB x1", + "kind": "gpu", + "compute": { "vcpu": 26, "arch": "x86_64" }, + "memory": { "ram_mb": 229376 }, + "gpu": { "model": "NVIDIA H100 SXM", "count": 1, "vram_mb": 81920, "access": "passthrough" }, + "price": { "amount": 2.49, "currency": "USD", "interval": "hour" }, + "stock": "in_stock" +} +``` + +A peer-to-peer market lists each ask the same way with `model: p2p` on the offer, and the market's `updated` on the offer says how fresh the price is. A provider whose file is only accelerator offers may serve it at `/.well-known/opengpu.json`; the shape is OpenServer's and the name says what is in it. A reader that only wants accelerators filters on the presence of a `gpu` block or `kind: gpu`. + +## What a directory does with it + +1. **Keeps the model string and normalises beside it.** `NVIDIA H100 SXM` and `H100-SXM5-80GB` are the same card; the directory matches them for search and shows the provider's spelling. +2. **Shows access beside VRAM.** A MIG slice of an H100 and a whole H100 share a model and differ in everything else. +3. **Computes the per-device price** from `price` and `count`, so a row for 8 cards and a row for 1 sort together, and says it did. +4. **Reads stock with its timestamp.** GPU stock is the field that goes stale first, and a directory shows when each row was read. + +## What is deliberately absent + +**No benchmarks or TFLOPS.** Vendor throughput figures depend on precision, sparsity and clock, and no two vendors quote them alike. A provider that wants to state them does so under its own key; a directory that measures publishes its own numbers under its own name. + +**No reservation calendar.** Whether a card is free next Tuesday is the provider's scheduler. `stock` says now. + +**No spot or preemptible flag.** OpenServer's `price.commitment` and the offer's `url` carry the terms; a provider selling the same card at a spot price lists a second offer. + +## Related standards + +- [OpenServer](/docs/openserver): the descriptor, the `gpu` kind and the offer this block sits in. +- [OpenCPU](/docs/opencpu), [OpenMemory](/docs/openmemory), [OpenDisk](/docs/opendisk), [OpenBandwidth](/docs/openbandwidth): the other four resources of a purchase, each with the same `range` shape. +- [OpenSwarm](/openswarm) and c0mpute: a peer renting its card lists it with this block and settles under OpenSwarm. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the gpu block, four access modes, interconnects, the range, position inside or beside `compute`. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/openmemory.md b/docs/openmemory.md new file mode 100644 index 0000000..a5ae3ed --- /dev/null +++ b/docs/openmemory.md @@ -0,0 +1,106 @@ +# OpenMemory + +OpenMemory is the shape of one resource in a server purchase: the memory. It says how much RAM an offer has, what kind, whether it is error-corrected, whether it is reserved for the buyer or balloonable, and how much more a buyer may add at checkout and for how much. It is the `memory` block of an [OpenServer](/docs/openserver) offer, written down on its own so a configurator that sells RAM by the gigabyte, a provider that sells memory-optimised instances and a directory that filters on it all mean the same thing by the same key. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface. + +Status: **0.1**. One of five resource specifications under OpenServer: [OpenCPU](/docs/opencpu), OpenMemory, [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu) and [OpenBandwidth](/docs/openbandwidth). Each describes one thing that is negotiable when a server is bought. + +Slug: `openmemory` + +## The problem + +Memory is the resource most often bought in increments and least often described. A dedicated-server configurator offers 64, 128 or 256 GB at three prices, and the difference between them is the whole reason to pick the box, but a scraper sees the default and a directory lists one number. "8 GB" on a VPS may be 8 GiB reserved, or 8 GB of which the host reclaims half under pressure. Whether the DIMMs are error-corrected decides whether a database belongs on the machine, and almost no listing says. + +The memory line of a spec sheet is short. This document makes it say what it means. + +## Terms + +- The **memory block** is the `memory` object on an OpenServer offer, or the same object served on its own. +- **Reserved** memory is backed by physical memory the host will not reclaim. **Balloonable** memory may be reclaimed by the host under pressure. **Shared** memory is oversubscribed with neighbours. +- A **range** is what the buyer may change at checkout, with the price of changing it. + +## The memory block + +```json +{ + "memory": { + "ram_mb": 65536, + "type": "DDR5", + "ecc": true, + "speed_mts": 4800, + "channels": 8, + "allocation": "reserved", + "swap_mb": 0, + "hugepages": true, + "range": { + "key": "ram_mb", + "min": 32768, + "max": 1048576, + "step": 32768, + "price": { "amount": 12, "currency": "USD", "interval": "month", "per": 32768 } + } + } +} +``` + +The smallest valid block states the size: + +```json +{ "memory": { "ram_mb": 8192 } } +``` + +The rules, and every one degrades: + +1. **`ram_mb` is required.** It is the memory the guest sees, in mebibytes, the same unit OpenServer uses in `compute.ram_mb`. 8 GiB is 8192. A provider that sells in decimal gigabytes converts once when it writes the file, so every reader adds the same numbers. +2. **`type`** is the memory technology as the vendor names it: `DDR4`, `DDR5`, `LPDDR5`, `HBM3`, or the provider's own word. `speed_mts` is the rated transfer rate in megatransfers per second. `channels` is the populated channel count on a dedicated box. All three are stated, not measured. +3. **`ecc`** is a boolean. Absent means unstated, and a directory that lets a buyer filter on ECC shows unstated rows as unstated, never as false. +4. **`allocation`** is one of `reserved`, `balloonable`, `shared`. Absent means unstated. A dedicated server is `reserved` by nature and may say so. +5. **`swap_mb`** is swap the provider configures by default, in mebibytes; `0` means none and absent means unstated. **`hugepages`** is whether the buyer may use huge pages, a boolean. +6. **`range`** is the negotiable part. `key` is `ram_mb`, `min`, `max` and `step` bound it in mebibytes, and `price` is the cost per `per` mebibytes at the offer's interval, on top of the base price. The example above says memory is sold in 32 GiB steps at 12 USD a month each. An offer with no `range` is sold as stated. +7. **This block wins over `compute.ram_mb`.** OpenServer 0.1 puts `ram_mb` inside `compute`. A provider may keep it there for readers that predate this document; when a `memory` block is present, its `ram_mb` is the one a reader uses. +8. **Unknown keys are kept.** A provider may say more; a reader passes it through under the provider's key. + +## Memory as its own offer + +Memory is rarely sold alone, but it is sold as an increment, and the increment is an offer: a RAM upgrade on a configurator, a memory-optimised tier that differs from the base tier only here, a reservation of memory on a platform that bills it separately from compute. Each is an OpenServer offer with a `memory` block and a `price`: + +```json +{ + "id": "ram-32", + "name": "32 GB RAM upgrade", + "kind": "dedicated", + "memory": { "ram_mb": 32768, "type": "DDR5", "ecc": true }, + "price": { "amount": 12, "currency": "USD", "interval": "month" } +} +``` + +A provider whose file is only memory offers may serve it at `/.well-known/openmemory.json`; the shape is OpenServer's and the name says what is in it. A reader that only wants memory filters offers on the presence of a `memory` block. + +## What a directory does with it + +1. **Shows mebibytes as the provider's unit.** Store `ram_mb`; display 8 GiB or 8 GB as the provider's page does, and say which. +2. **Filters on ECC as three states**, yes, no and unstated. +3. **Prices the range.** A configurator's 64, 128 and 256 GB choices are one offer with a range, and a directory shows the base and the step price rather than three rows. +4. **Reads `memory` before `compute.ram_mb`** and never sums the two. + +## What is deliberately absent + +**No bandwidth or latency figures.** `speed_mts` is the DIMM rating. Measured memory bandwidth is a benchmark, and benchmarks are another document's business. + +**No persistent memory tier.** Optane-style persistent memory and CXL-attached memory are storage or memory depending on the provider; a provider states them under its own key until there is a second one to agree with. + +**No per-process limits.** cgroup memory limits on a container platform are the platform's terms, linked from the offer's `url`. + +## Related standards + +- [OpenServer](/docs/openserver): the descriptor and the offer this block sits in. +- [OpenCPU](/docs/opencpu), [OpenDisk](/docs/opendisk), [OpenGPU](/docs/opengpu), [OpenBandwidth](/docs/openbandwidth): the other four resources of a purchase, each with the same `range` shape. + +## Version history + +| Version | Date | Change | +|---|---|---| +| 0.1 | 2026-09-13 | First publication: the memory block, ECC as three states, three allocations, the range, precedence over `compute.ram_mb`. | + +## License + +The specification text is CC BY 4.0. Serve it, copy it, extend it. diff --git a/docs/openswarm.md b/docs/openswarm.md index aedd2ae..e314125 100644 --- a/docs/openswarm.md +++ b/docs/openswarm.md @@ -38,6 +38,8 @@ a different product. The member protocols keep their `ip` names. | `paid2seed` | Server protocol for paid seeding: leases, storage challenges and probes over the wire, GiB-month settlement, the seeder client | [`paid2seed.md`](./openswarm/paid2seed.md) | | `pay2stream` | Client protocol for paid live streams: consent for channels, relay offers, tickets and listings, watching as a peer or on any HLS player | [`pay2stream.md`](./openswarm/pay2stream.md) | | `paid2stream` | Server protocol for paid live streams: relay leases per hour, presence proofs, gateways serving standard HLS, M3U and EPG | [`paid2stream.md`](./openswarm/paid2stream.md) | +| OpenFile | The web door onto an `ipfile` swarm: `/.well-known/openfile.json` lists a publisher's files with fetch routes, verification, consent, price and holders | [`openfile.md`](./openfile.md) | +| OpenDisk | The web door onto a `paid2seed` seeder: `/.well-known/opendisk.json` lists free GiB, price, policy, proof cadence and hub standing, so a disk is found instead of waited for | [`opendisk.md`](./opendisk.md) | Supporting documents: