logicsrc/docs/opendisk.md
Anthony Ettinger 9f941fca42
OpenFile and OpenDisk carry OpenServer 0.2's developer block, and OpenFile names fi1zes.com (#173)
A publisher and a disk each say which CLI drives them, in the same
`developer` shape OpenServer 0.2 defines, so a marketplace shows one
install line beside every listing without reading a docs page per host.
fi1zes.com is the name chosen for the OpenFile marketplace on 2026-09-13,
not yet registered; bittorrented.com stays the reference reader until then.


Claude-Session: https://claude.ai/code/session_01Khk1C6Ese6xjdHAWLVstca

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 22:05:06 -07:00

192 lines
15 KiB
Markdown

# 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"],
"developer": {
"cli": {
"name": "ip",
"install": { "curl": "curl -fsSL https://d1sks.com/install.sh | sh" },
"docs": "https://d1sks.com/docs/cli"
}
},
"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.
2a. **`developer`** is the block [OpenServer](/docs/openserver) 0.2 defines, unchanged: the CLI a requester uses to rent this disk and a seeder uses to run it (`cli.name`, `cli.install` keyed by package manager with the command as the guide prints it, `cli.docs`, `cli.repo`), plus `api_docs` and `github` where they exist. A disk usually names the client its hub ships, so a marketplace can show one install line beside every disk it lists; `"cli": null` says there is none.
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/<key>` 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.