mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-02 20:57:03 +00:00
Merge remote-tracking branch 'origin/master' into openniche-coupon-recipe
# Conflicts: # apps/logicsrc-web/src/app/llms.txt/route.ts # apps/logicsrc-web/src/app/sitemap.ts # apps/logicsrc-web/src/components/site-shell.tsx # apps/logicsrc-web/src/lib/docs.ts
This commit is contained in:
commit
271b00f137
18 changed files with 1682 additions and 0 deletions
|
|
@ -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.
|
||||
|
|
|
|||
63
apps/logicsrc-web/src/app/openbandwidth/page.tsx
Normal file
63
apps/logicsrc-web/src/app/openbandwidth/page.tsx
Normal file
|
|
@ -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 <ResourceSpecPage spec={SPEC} />;
|
||||
}
|
||||
61
apps/logicsrc-web/src/app/opencpu/page.tsx
Normal file
61
apps/logicsrc-web/src/app/opencpu/page.tsx
Normal file
|
|
@ -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 <ResourceSpecPage spec={SPEC} />;
|
||||
}
|
||||
195
apps/logicsrc-web/src/app/opendisk/page.tsx
Normal file
195
apps/logicsrc-web/src/app/opendisk/page.tsx
Normal file
|
|
@ -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 (
|
||||
<SiteShell active="OpenDisk">
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<p className="eyebrow">LogicSRC standards surface</p>
|
||||
<h2>OpenDisk</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<p style={{ color: "#41505d" }}>
|
||||
<Link href="/openswarm">OpenSwarm</Link> 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{" "}
|
||||
<code style={mono}>/.well-known/opendisk.json</code>, and a requester reads the disk
|
||||
before it posts.
|
||||
</p>
|
||||
<p style={{ color: "#5b6b7a" }}>
|
||||
Status: 0.1. <a href="https://d1sks.com">d1sks.com</a> 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{" "}
|
||||
<Link href="/openserver">OpenServer</Link> sense, listed as one at nichedb.dev.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>The descriptor</h2>
|
||||
<p>
|
||||
Served at <code style={mono}>/.well-known/opendisk.json</code>. Only a name, the free
|
||||
space and a price are required.
|
||||
</p>
|
||||
</div>
|
||||
<pre style={pre}>{DESCRIPTOR}</pre>
|
||||
<p style={{ color: "#41505d" }}>
|
||||
<code style={mono}>key</code> is the seeder identity that signs proofs and is paid;{" "}
|
||||
<code style={mono}>hubs</code> is where it takes leases. <code style={mono}>accepts</code>{" "}
|
||||
is the operator's policy stated up front, so nobody posts an offer this disk would never
|
||||
take. <code style={mono}>record.source</code> 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:
|
||||
</p>
|
||||
<pre style={pre}>{MINIMAL}</pre>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Renting a disk</h2>
|
||||
<p>Nothing on the swarm side changes. The descriptor only moves the first step to a URL.</p>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{RENT.map(([step, what]) => (
|
||||
<tr key={step}>
|
||||
<td style={td}>
|
||||
<strong>{step}</strong>
|
||||
</td>
|
||||
<td style={td}>{what}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>As an OpenServer offer</h2>
|
||||
<p>A disk is a hosting offer, and a directory that reads OpenServer lists it without a second parser.</p>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={th}>OpenServer</th>
|
||||
<th style={th}>from OpenDisk</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{MAPPING.map(([key, from]) => (
|
||||
<tr key={key}>
|
||||
<td style={td}>
|
||||
<code style={mono}>{key}</code>
|
||||
</td>
|
||||
<td style={td}>{from}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<p style={{ color: "#41505d", marginTop: "1rem" }}>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>What is deliberately absent</h2>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{ABSENT.map(([what, why]) => (
|
||||
<tr key={what}>
|
||||
<td style={td}>
|
||||
<strong>{what}</strong>
|
||||
</td>
|
||||
<td style={td}>{why}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Where everything lives</h2>
|
||||
</div>
|
||||
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
|
||||
<li>
|
||||
<Link href="/docs/opendisk">Specification</Link>: the descriptor, holding, renting a disk,
|
||||
marketplaces, the OpenServer mapping
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://d1sks.com">d1sks.com</a>: the reference marketplace
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/openswarm">OpenSwarm</Link>: pay2seed for the offer, paid2seed for leases and
|
||||
proofs, ippay for the payee, ipfile for what is held
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/openfile">OpenFile</Link>, the publisher's side and the holders list a disk
|
||||
appears in; <Link href="/openserver">OpenServer</Link>, the hosting offer a disk maps onto
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</SiteShell>
|
||||
);
|
||||
}
|
||||
220
apps/logicsrc-web/src/app/openfile/page.tsx
Normal file
220
apps/logicsrc-web/src/app/openfile/page.tsx
Normal file
|
|
@ -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 (
|
||||
<SiteShell active="OpenFile">
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<p className="eyebrow">LogicSRC standards surface</p>
|
||||
<h2>OpenFile</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<p style={{ color: "#41505d" }}>
|
||||
A file on a swarm is findable by its infohash and by nothing else. <Link href="/openswarm">OpenSwarm</Link>{" "}
|
||||
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{" "}
|
||||
<code style={mono}>/.well-known/openfile.json</code>, 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.
|
||||
</p>
|
||||
<p style={{ color: "#5b6b7a" }}>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>The descriptor</h2>
|
||||
<p>
|
||||
Served at <code style={mono}>/.well-known/openfile.json</code>. Only the publisher's
|
||||
name and each file's content hash and name are required.
|
||||
</p>
|
||||
</div>
|
||||
<pre style={pre}>{DESCRIPTOR}</pre>
|
||||
<p style={{ color: "#41505d" }}>
|
||||
<code style={mono}>id</code> is the SHA-256 of the plaintext, which for an ipfile swarm is
|
||||
the manifest's <code style={mono}>plainRoot</code>: 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. <code style={mono}>encryption</code> is ipfile unless the
|
||||
publisher says <code style={mono}>none</code> as an explicit act. The smallest valid
|
||||
descriptor:
|
||||
</p>
|
||||
<pre style={pre}>{MINIMAL}</pre>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Five ways to fetch</h2>
|
||||
<p>Listed in the publisher's order of preference. A reader picks the first kind it speaks.</p>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={th}>kind</th>
|
||||
<th style={th}>What it is</th>
|
||||
<th style={th}>Who uses it</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{FETCH.map(([kind, what, who]) => (
|
||||
<tr key={kind}>
|
||||
<td style={td}>
|
||||
<code style={mono}>{kind}</code>
|
||||
</td>
|
||||
<td style={td}>{what}</td>
|
||||
<td style={td}>{who}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<p style={{ color: "#41505d", marginTop: "1rem" }}>
|
||||
Over any of them the bytes verify the same way: each piece against the plaintext piece
|
||||
layer, or the whole against <code style={mono}>id</code>. A reader refuses bytes that fail
|
||||
and says which holder served them.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Holders</h2>
|
||||
<p>A file is only as available as the machines holding it.</p>
|
||||
</div>
|
||||
<p style={{ color: "#41505d" }}>
|
||||
<code style={mono}>holders</code> answers with who has the bytes now: a{" "}
|
||||
<code style={mono}>seeder</code> with a lease and the time it last passed a proof, a{" "}
|
||||
<code style={mono}>gateway</code> serving it over HTTP, a <code style={mono}>peer</code>{" "}
|
||||
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 <Link href="/opendisk">OpenDisk</Link> descriptor, so a
|
||||
publisher that likes a holder can buy more of it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>What is reused</h2>
|
||||
<p>Every record OpenFile points at already has a name in the OpenSwarm family.</p>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{REUSED.map(([what, from]) => (
|
||||
<tr key={what}>
|
||||
<td style={td}>
|
||||
<strong>{what}</strong>
|
||||
</td>
|
||||
<td style={td}>{from}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>What is deliberately absent</h2>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{ABSENT.map(([what, why]) => (
|
||||
<tr key={what}>
|
||||
<td style={td}>
|
||||
<strong>{what}</strong>
|
||||
</td>
|
||||
<td style={td}>{why}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Where everything lives</h2>
|
||||
</div>
|
||||
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
|
||||
<li>
|
||||
<Link href="/docs/openfile">Specification</Link>: the descriptor, the per-file descriptor,
|
||||
holders, discovery, fetching and verifying, what a directory owes a publisher
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/openswarm">OpenSwarm</Link>: ipfile, pay2seed, paid2seed, ippay and ipdb,
|
||||
the records this file is a door onto
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/opendisk">OpenDisk</Link>, what a holder serves about the disk it rents;{" "}
|
||||
<Link href="/openserver">OpenServer</Link>, how a gateway or seeder is listed as hosting
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/openprofile">OpenProfile.md</Link>, the operator behind a publisher
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</SiteShell>
|
||||
);
|
||||
}
|
||||
61
apps/logicsrc-web/src/app/opengpu/page.tsx
Normal file
61
apps/logicsrc-web/src/app/opengpu/page.tsx
Normal file
|
|
@ -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 <ResourceSpecPage spec={SPEC} />;
|
||||
}
|
||||
59
apps/logicsrc-web/src/app/openmemory/page.tsx
Normal file
59
apps/logicsrc-web/src/app/openmemory/page.tsx
Normal file
|
|
@ -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 <ResourceSpecPage spec={SPEC} />;
|
||||
}
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
167
apps/logicsrc-web/src/components/resource-spec-page.tsx
Normal file
167
apps/logicsrc-web/src/components/resource-spec-page.tsx
Normal file
|
|
@ -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 (
|
||||
<SiteShell active={spec.name}>
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<p className="eyebrow">LogicSRC standards surface · OpenServer resource</p>
|
||||
<h2>{spec.name}</h2>
|
||||
<p>{spec.tagline}</p>
|
||||
</div>
|
||||
<p style={{ color: "#41505d" }}>{spec.problem}</p>
|
||||
<p style={{ color: "#5b6b7a" }}>
|
||||
Status: 0.1. The <code style={mono}>{spec.block}</code> block of an{" "}
|
||||
<Link href="/docs/openserver">OpenServer</Link> 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{" "}
|
||||
<code style={mono}>{wellKnown}</code>. Every rule degrades: the smallest valid block is{" "}
|
||||
<code style={mono}>{spec.smallest}</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>The {spec.block} block</h2>
|
||||
<p>
|
||||
Every key is the provider's claim, in fixed units. <code style={mono}>range</code>{" "}
|
||||
is the part a buyer can dial at checkout: the field, its bounds, the step and what a
|
||||
step costs.
|
||||
</p>
|
||||
</div>
|
||||
<pre style={pre}>{spec.sample}</pre>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Fields</h2>
|
||||
<p>Absent means unstated, never a default. A reader says so beside the number.</p>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={th}>Key</th>
|
||||
<th style={th}>Values</th>
|
||||
<th style={th}>Meaning</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{spec.fields.map(([key, values, meaning]) => (
|
||||
<tr key={key}>
|
||||
<td style={td}>
|
||||
<code style={mono}>{key}</code>
|
||||
</td>
|
||||
<td style={td}>{values}</td>
|
||||
<td style={td}>{meaning}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>What a directory does with it</h2>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{spec.directory.map(([what, how]) => (
|
||||
<tr key={what}>
|
||||
<td style={td}>
|
||||
<strong>{what}</strong>
|
||||
</td>
|
||||
<td style={td}>{how}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>What is deliberately absent</h2>
|
||||
</div>
|
||||
<table style={table}>
|
||||
<tbody>
|
||||
{spec.absent.map(([what, why]) => (
|
||||
<tr key={what}>
|
||||
<td style={td}>
|
||||
<strong>{what}</strong>
|
||||
</td>
|
||||
<td style={td}>{why}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="band">
|
||||
<div className="section-head">
|
||||
<h2>Where everything lives</h2>
|
||||
</div>
|
||||
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
|
||||
<li>
|
||||
<Link href={`/docs/${spec.slug}`}>Specification</Link>: the block, every rule and how
|
||||
it degrades, the resource as its own offer, what a directory owes a provider
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/docs/openserver">OpenServer</Link>: the descriptor at{" "}
|
||||
<code style={mono}>/.well-known/openserver.json</code> that this block sits in, and the
|
||||
directory that reads it
|
||||
</li>
|
||||
<li>
|
||||
The five resources of a purchase:{" "}
|
||||
{SIBLINGS.map(([name, slug], i) => (
|
||||
<span key={slug}>
|
||||
{i > 0 ? ", " : ""}
|
||||
{slug === spec.slug ? <strong>{name}</strong> : <Link href={`/${slug}`}>{name}</Link>}
|
||||
</span>
|
||||
))}
|
||||
, each with the same <code style={mono}>range</code> shape
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</SiteShell>
|
||||
);
|
||||
}
|
||||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue