OpenObject is a bucket you can mount: keyed objects as ipfile swarms placed on OpenDisk disks under pay2seed at a stated redundancy (three replicas on three operators in two countries by default), an ipdb index as the bucket's clock, a repair loop, an HTTP API, an S3 mapping and a mount whose consistency is close-to-open by seq. d1sks.com is the reference store. OpenSlice is a container whose compute is rented from one market and whose disk is mounted from another: a host descriptor at /.well-known/openslice.json, one signed slice file (image by digest, OpenCPU/OpenMemory/OpenGPU units, OpenObject mounts, OpenCreds env, ports, placement, lifetime), and a reservation that is a paid2seed lease applied to compute with presence proofs per epoch. slic3s.com is the reference marketplace; c0mpute hosts take the slice role. Both are registered under OpenServer in the catalogs family with landing pages, rows in the OpenSwarm family table, and the spec-discovery contract. llms.txt now cites the specification URL for child specs too, which every block under OpenServer had been missing. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
25 KiB
OpenObject
OpenObject is a bucket you can mount. It is one file a store serves about the object storage it runs, one record for a bucket, one record per object, and the rules that turn a pile of rented disks into a place where keyed objects are kept at a stated redundancy, verified every period, repaired when a disk fails, and read back by path from anywhere. The disks are OpenDisk disks paid under OpenSwarm leases; the bytes on them are ipfile ciphertext the owner alone can read; the store is whoever keeps the index and posts the offers. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface, with d1sks.com as the reference store, running every bucket at three replicas by default.
Status: 0.1. A description of the shape a store, a client and a mount agree on, published so a bucket can move between stores and a disk can hold objects for any of them.
Slug: openobject
The problem
The pieces under this document already exist. pay2seed says how one swarm is paid to be held, paid2seed says how a holder proves it still holds it, OpenDisk says how a disk is found, OpenFile says how a publisher lists the files it has released. c0mpute hosts files as blake3-addressed objects behind Reed-Solomon 10 data and 4 parity shards, on its own network, through its own routes.
What none of them says is what an application needs: a bucket with a name, a thousand keys under it that change, a promise that each object is on three different machines in two different countries, a read by key that comes back verified, a list by prefix, a version history, and a mount so a process can open /data/photos/2026/09/a.jpg without knowing any of the above. Every store that has built this has built it once, for itself, in a shape only its own client reads. OpenObject is that shape written down, so a bucket at one store is the same bucket at another, and a disk holding objects for one store is holding the same records for all of them.
Terms
- A store is the service that keeps a bucket's index, places its objects on disks, watches their proofs and repairs them. It is a gateway, not a holder; it may hold nothing itself. d1sks.com is the reference. Its descriptor is the file it serves about itself.
- A bucket is a named set of objects under one key, with one placement policy. The bucket key signs the index.
- An object is bytes under a key, where a key is a UTF-8 path such as
photos/2026/09/a.jpg. An object has revisions; the newest is what a plain read returns. - A disk is an OpenDisk disk. A holder is a disk holding one object under a
paid2seedlease. - The policy is the redundancy a bucket asks for:
replicas(every holder has the whole object) orerasure(every holder has one shard). - A mount is a file-system view of a bucket on a client machine, or inside an OpenSlice slice.
- The owner is who holds the bucket key and the content keys, and whose escrow at a hub pays the disks.
The store descriptor
A store serves a JSON document at /.well-known/openobject.json on its own origin.
{
"name": "d1sks.com",
"web": "https://d1sks.com",
"operator": "https://profullstack.com/.well-known/openprofile.md",
"key": "ed25519:7c02e4a19f5d3b8c6e1a0f4d7b2c9e8a5f3d1c0b4e7a6f9d2c5b8e1a4f7d0c3b",
"api": "https://d1sks.com/openobject/v1",
"hubs": ["https://d1sks.com/api/openswarm", "https://bittorrented.com/api/openswarm"],
"disks": "https://d1sks.com/api/disks",
"developer": {
"cli": {
"name": "ip",
"install": { "curl": "curl -fsSL https://d1sks.com/install.sh | sh" },
"docs": "https://d1sks.com/docs/cli"
},
"api_docs": "https://d1sks.com/docs/openobject"
},
"updated": "2026-09-21T06:00:00Z",
"policies": [
{ "id": "3x", "mode": "replicas", "replicas": 3, "min_operators": 3, "min_countries": 2, "default": true },
{ "id": "2x", "mode": "replicas", "replicas": 2, "min_operators": 2 },
{ "id": "rs-10-4", "mode": "erasure", "k": 10, "parity": 4, "min_operators": 14 }
],
"price": {
"currency": "USD",
"per_gib_month": { "3x": 0.036, "2x": 0.024, "rs-10-4": 0.017 },
"per_gib_transfer": 0.005,
"per_10k_ops": 0.004,
"min_gib": 0,
"max_object_gib": 500
},
"location": { "countries": ["DE", "FI", "US", "PT", "SG"] },
"capacity": { "free_gib": 184000, "disks": 412 },
"accepts": { "encryption": ["owner", "store"], "versioning": true, "public_reads": true, "mounts": ["fuse", "nfs", "webdav", "s3"] },
"proof": { "every_hours": 6, "repair_within_hours": 24 },
"record": { "source": "https://d1sks.com/api/openswarm/pay2seed/requesters/ed25519:7c02…0c3b", "buckets": 1930, "objects": 41200000, "lost": 0, "since": "2026-09-13T00:00:00Z" },
"s3": "https://s3.d1sks.com"
}
The smallest valid descriptor is a name, an API and one policy:
{ "name": "spare closet", "api": "https://closet.example/openobject/v1", "policies": [{ "id": "3x", "mode": "replicas", "replicas": 3 }] }
The rules, and every one degrades:
name,apiand one entry inpoliciesare the only required keys. A reader lists what it was given and reports the rest as unstated.operatoris who is answerable, as an OpenProfile.md URL.keyis the store's OpenSwarm identity, the requester key it postspay2seedoffers under and the key a disk sees on every lease.hubsare where it posts. A store with no key is a store that places nothing and only indexes;apisays what it does.disksis where the store's pool is listed: a URL answering with the OpenDisk descriptors it places onto, or the descriptors inline. An owner reads the pool before trusting a policy that promises three operators.developeris the block OpenServer 0.2 defines, unchanged.policiesare what the store will run.modeisreplicasorerasure. Forreplicas,replicasis how many holders each have the whole object. Forerasure,kdata shards andparityshards, one per holder, and anykrebuild the object.min_operatorsandmin_countriesare floors on distinctness across a policy's holders, counted on the disks'operatorandlocation.countries. One policy isdefault: true; a bucket that names none gets it. Three replicas on three operators in two countries is the policy this document means by "3x", and it is the reference store's default.priceis per policy for keeping, one number for serving and one for operations.per_gib_monthfor a policy is what the owner pays per GiB of plaintext, so a3xprice already covers three disks; a store passes the disks'per_gib_monththrough with its margin.per_10k_opsprices index operations (PUT, DELETE, LIST); reads of bytes areper_gib_transfer.max_object_gibcaps one object.locationandcapacityare the pool summarised: the countries its disks are in and the free GiB across them.capacity.disksis the count.acceptsis policy.encryption: ownermeans the owner encrypts before upload and the store never has a content key;storemeans the store holds keys for the owner, which is what a public bucket or an S3 client needs.mountslists what the store can serve a bucket as.proofis the cadence the store leases at (pay2seedproof.everyHours) and the longest it lets an object stay below policy before a replacement holder has been leased.recordis the store's standing as a requester at its hubs,sourcethe hub's own page; the numbers are copied for convenience and a reader that ranks on them reads the hub.lostis objects that went below the read threshold, ever, and a store that has lost one says so.s3is an S3-compatible endpoint when the store serves one, see below.- Unknown keys are kept.
Serve it as application/json. The descriptor is a claim; that it came from the store's own origin is one verification, and the hub's record under key is the other.
The bucket
A bucket is a record the store keeps at <api>/b/<bucket> and the owner may keep anywhere.
{
"openobject": "0.1",
"type": "openobject.bucket",
"id": "ed25519:3a9f0c2e7d1b5a8c4f6e2d0b9a7c1e3f5d8b0a2c4e6f8d1b3a5c7e9f0d2b4a6c",
"name": "photos",
"owner": "https://ana.example/.well-known/openprofile.md",
"store": "https://d1sks.com/openobject/v1",
"policy": { "id": "3x", "mode": "replicas", "replicas": 3, "min_operators": 3, "min_countries": 2 },
"encryption": { "by": "owner", "box": "x25519:4c40…18c3" },
"versioning": { "keep": 10, "days": 90 },
"public": false,
"index": "ed25519:3a9f…4a6c/index",
"budget": { "hub": "https://d1sks.com/api/openswarm", "payment": { "network": "eip155:8453" } },
"created": "2026-09-13T10:00:00Z",
"updated": "2026-09-21T06:14:02Z",
"sigs": [{ "alg": "ed25519", "key": "ed25519:3a9f…4a6c", "sig": "…" }]
}
idis the bucket key, an Ed25519 public key. The bucket is whoever holds the private half. Names are per owner and per store; the key is what a disk, a hub and a second store agree on.policyis one of the store's policies, copied in full at creation so the bucket carries its own promise when it moves.encryption.byisownerorstore. Withowner, each object is anipfileswarm whose per-file key is derived from a bucket master key the owner holds,keyDerivation: derivedasipfiledefines it, andboxis the bucket's X25519 key that grants are boxed to. Withstore, the store holds the master key and serves plaintext to authorised readers.versioningkeeps the lastkeeprevisions of a key or those newer thandays, whichever is more.{ "keep": 1 }is a bucket with no history. A revision kept is a revision still leased and paid for.public: truemeans any reader may GET and LIST without credentials. A public bucket isencryption: storeor holds plaintext swarms.indexis anipdbfeed under the bucket key: the append-only log of object records, one entry per revision and per tombstone. A store replicates it; a second store rebuilds the bucket from it; a mount follows it.budgetnames the hub and network the owner's escrow sits at. The store spends it on leases and repairs underpay2seedas the owner's delegate, and the hub's ledger for the requester is the bill.
The object
One record per revision, appended to the index and returned by HEAD.
{
"openobject": "0.1",
"type": "openobject.object",
"bucket": "ed25519:3a9f…4a6c",
"key": "photos/2026/09/a.jpg",
"rev": 3,
"seq": 48211,
"id": "sha256:d6c3f8285b7871d6a400cba14408288a9acde679f12e1e7dc276f29ca7c493ff",
"size": 4194304,
"contentType": "image/jpeg",
"meta": { "camera": "X100VI" },
"swarm": { "manifest": "sha256:9ab1…", "infohashV2": "sha256:4b74…a342", "cipherRoot": "sha256:ba8c…6f7a" },
"inline": null,
"policy": { "mode": "replicas", "replicas": 3 },
"holders": [
{ "disk": "ed25519:a41e…7b0a", "lease": "sha256:5c02…", "since": "2026-09-20T18:30:00Z", "provenAt": "2026-09-21T06:00:05Z", "shard": null },
{ "disk": "ed25519:9d3c…11ef", "lease": "sha256:71aa…", "since": "2026-09-20T18:30:04Z", "provenAt": "2026-09-21T06:00:09Z", "shard": null },
{ "disk": "ed25519:02be…c8d0", "lease": "sha256:c3f0…", "since": "2026-09-20T18:30:07Z", "provenAt": "2026-09-21T05:59:58Z", "shard": null }
],
"state": "healthy",
"tombstone": false,
"created": "2026-09-20T18:29:51Z",
"sigs": [{ "alg": "ed25519", "key": "ed25519:3a9f…4a6c", "sig": "…" }]
}
keyis the path./separates segments, a key never starts with/, and there are no directories: a prefix that other keys share is listed as one, and that is all a directory is.revcounts revisions of this key from 1.seqis the entry's position in the bucket's index and is the bucket's clock: the record with the higherseqis the newer statement about a key, whoever wrote it and whenever it was signed.idis the SHA-256 of the plaintext, theplainRootof the swarm, and the object's ETag. Two revisions with oneidare one swarm held once, so a rewrite of unchanged bytes costs an index entry and nothing else.swarmnames theipfilemanifest the bytes live in. An object ofsizeunder 65,536 bytes may instead be carried ininline, base64 in the index entry, with no swarm and no holders; the index's own replication is its redundancy. A store may pack many small objects into one swarm and recordswarm.rangefor each; the record still carries its ownid.holdersare the disks under lease, one per replica or per shard,shardthe shard index in erasure mode.provenAtis the last proof the store saw, frompaid2seedreceipts.stateishealthywhen every holder the policy asks for is leased and proven withinproof.every_hours;degradedwhen fewer are but the object can still be read (at least one replica, or at leastkshards);lostbelow that;pendingbefore the first proof. A read of adegradedobject succeeds; a store reports the state on everyHEAD.tombstone: trueis a deletion: the newest record for a key says there is no object. Earlier revisions stay readable byrevuntilversioninglets them go.- The record is signed by the bucket key when the owner wrote it and by the store key when the store did on the owner's behalf; either is valid, and a reader that finds neither treats the record as hearsay.
Placement
What the policy means, and what the store must do to say healthy:
- Distinct disks. Every holder is a different OpenDisk
key. Never two replicas on one seeder. - Distinct operators, at least
min_operators, counted on the disks'operator; a disk with no operator counts as its own and no more. - Distinct countries, at least
min_countries, counted onlocation.countries. - Offers, not uploads. The store posts one
pay2seedoffer per object withseeders.minset toreplicas(ork + parity) andsubjectthe swarm,visibility: privateunless the bucket is public, from the owner's escrow. Disks take leases aspaid2seedsays; the store fills from its pool first by posting to the disks' own hubs. Nothing in the swarm side changes. - The first copy comes from the client. The client seeds the swarm, or uploads to the store's gateway which seeds it, until
replicasholders have fetched and proven. A store that accepts the bytes and answers before that reportsstate: pending, and an honest client keeps its copy untilhealthy. - Locality is a preference, not a policy. A store may place near where reads come from, or near the c0mpute nodes that will read the bucket, but never at the expense of the floors.
Repair
- A holder whose challenge or probe fails, or whose lease ends without renewal, is
failedon the record at once. - The store posts a replacement offer within
proof.repair_within_hours, sourced from any healthy holder, and appends a record with the new holder when its first proof lands. The object isdegradedmeanwhile, never silently short. - Repair spends the bucket's budget. A store that cannot repair because the escrow is empty says so on the record (
state: degraded,reason: budget) and to the owner by whatever channel it has; it never lets an object drop below policy without a record saying why. - In erasure mode, repair rebuilds one shard from any
kand places it on a new disk; it never re-encodes the whole object. - An object below the read threshold is
lost, the store'srecord.lostgoes up by one, and the record stays in the index so the loss is on the books.
Reading, writing, listing
The store's API under api. Bodies are application/json unless bytes.
| Method and path | Does |
|---|---|
PUT /b/<bucket> |
Create or update a bucket record. Body is the record, signed by the bucket key. |
GET /b/<bucket> |
The bucket record and its state summary: objects, GiB, healthy, degraded and lost counts. |
GET /b/<bucket>?prefix=&delimiter=/&after=&limit= |
List keys: the newest non-tombstone record per key under prefix; with delimiter, common prefixes collapse to one entry each. Cursor is after. |
PUT /b/<bucket>/<key> |
Write a revision. Body is the bytes (ciphertext when encryption.by: owner, with X-OpenObject-Manifest carrying the ipfile manifest) or, with Content-Type: application/vnd.openobject.object+json, a record for a swarm already seeded. If-Match: <etag> makes it conditional on the current id; If-None-Match: * makes it create-only. Answers 201 with the record and state. |
GET /b/<bucket>/<key> |
The newest revision's bytes, verified against id before the last byte is sent. Range is honoured. ?rev= reads an older revision. ETag is the id. X-OpenObject-State is the state. |
HEAD /b/<bucket>/<key> |
The record, as headers and as X-OpenObject-Record JSON. |
DELETE /b/<bucket>/<key> |
Append a tombstone. ?purge=1 also ends the leases of every revision, which is the only way bytes leave the disks before versioning lets them. |
GET /b/<bucket>/<key>?versions |
Every revision's record, newest first. |
GET /b/<bucket>.index?after=<seq> |
The index as an ipdb feed, for a mount or a second store to follow. |
POST /b/<bucket>/<key>:repair |
Ask for a repair pass now. |
GET /b/<bucket>/<key>:holders |
The holders with their OpenDisk descriptors, for a client that wants to fetch from the swarm itself. |
Authorisation is one of: a request signed by the bucket key (Authorization: OpenObject <sig> over method, path, date and body hash); an OpenAccess token with scope openobject:read or openobject:write and the bucket in its grant; nothing, on a public bucket's reads. A 409 on If-Match carries the current record. 402 on a write means the escrow will not cover the policy, with the shortfall.
Reads by a client that holds the content key are ipfile reads: fetch the ciphertext from any holder or the store's webseed, decrypt, verify id. A store reading for an encryption: store bucket does the same with its own key. Either way the plaintext hash is checked before it is served, and a mismatch is a 502 and a failed mark on the holder it came from.
Mounting
A mount turns a bucket into a directory. It is how an OpenSlice slice gets its disk and how a laptop gets its photos.
- Keys are paths. The mount shows
photos/2026/09/a.jpgat<mountpoint>/photos/2026/09/a.jpg. Directories are prefixes and exist while a key is under them. An empty directory is a key ending in/with no bytes. - Metadata is the index. The mount follows
<bucket>.indexand answersstat,readdirandopenfrom it, so a list never touches a disk. A mount that has the index fromseq48211 sees exactly the bucket at 48211. - Data is the swarm.
readfetches pieces from holders or the store's gateway, verifies, caches on local disk up to the mount's cache size. A slice on c0mpute reads from a holder on the same network with no internet egress. - Writes are write-back. A file written is a local revision until
close, then onePUT; the object ispendinguntil the store sayshealthy, and the mount keeps its copy until then.fsyncwaits forpendingto clear when the mount is--sync; otherwise it returns when the store has the bytes. - Consistency is close-to-open by
seq. Two mounts writing one key both succeed; the higherseqwins and the other becomes a revision. A mount that wants a lock usesIf-Matchand gets a409. There is no byte-range locking and no atomic rename across keys;renameis a copy of the record under the new key and a tombstone under the old, two index entries, and a reader may see the moment between them. - Owners' keys stay with the mount. With
encryption: owner, the mount holds the bucket master key and the store never sees plaintext. Inside a slice, the key is granted to the slice for its reservation, as OpenSlice says. - What a mount is not. Not a block device, not POSIX, not a database. A program that needs
O_APPEND,flock, sparse files or sub-secondmtimefrom two writers needs a local disk and should write its results to the bucket when it is done.
A store lists what it can serve a bucket as in accepts.mounts: fuse (the reference ip mount client), nfs and webdav (served by the store's gateway for machines that cannot run a client, plaintext only on encryption: store buckets or over a per-mount key the gateway holds for the session), and s3 below.
S3
A store may serve an S3-compatible endpoint at s3, because every tool already speaks it. Bucket name is the OpenObject bucket id or a name the store maps to one; object key is the key; ETag is the hex of id; x-amz-meta-* is meta; ListObjectsV2 is the list above; PutObject with If-None-Match: * is create-only; versions are S3 versions with VersionId equal to rev. Multipart upload becomes one swarm when completed. Credentials are an OpenAccess token as the secret key and its id as the access key, or a store-issued pair. An S3 client never sees holders or state; HeadObject carries x-amz-meta-openobject-state so a careful one can. encryption: owner buckets are not reachable over S3, because S3 has nowhere to put the key.
As an OpenServer offer
A store is a storage offer, and a directory that reads OpenServer lists it as one:
| OpenServer | from OpenObject |
|---|---|
provider.name, web, operator, developer |
the same keys |
offers[] |
one per policy: id the policy id, name the store's name and the policy, kind storage, model p2p, premises off-prem, management managed, tenancy shared |
offers[].price |
{ "amount": price.per_gib_month[policy], "currency", "interval": "month", "unit": "gib" } |
offers[].location |
location, unchanged |
offers[].stock |
in_stock while capacity.free_gib is above zero |
offers[].updated |
updated |
The disks under a store are listed separately as OpenDisk offers; a directory shows a store as one line and its pool as many.
On c0mpute
c0mpute's storage role already holds blake3-addressed objects as Reed-Solomon 10 data and 4 parity shards and serves them from a gateway. Mapped onto this document: the network is a store, rs-10-4 is its erasure policy, its gateway serves the API above, each storage worker is an OpenDisk disk, and ipfile.pin is the workload a placement becomes. What c0mpute gains is the bucket, the index and the mount, and what a bucket gains from c0mpute is locality: a slice or a transcode job reading a bucket held on the same network pays no internet egress, which is the network's whole argument for hosting files at all. An object.repair workload lets any verifier node run the repair pass and be paid per object restored.
What is deliberately absent
No settlement of its own. Every byte held is a pay2seed offer and a paid2seed lease, and every proof and payout is theirs. This document adds the index, the policy and the repair loop, nothing under them.
No plaintext at a disk. With encryption: owner a disk holds ciphertext it cannot read and a store holds an index it cannot decrypt. With encryption: store the owner has chosen to trust the store, and the disks still see nothing.
No central registry. A store is anyone with an index and a hub account. A bucket moves by handing a second store its record and its index; the disks and the leases do not change.
No POSIX. A mount is a bucket seen as paths. What it promises is in the mounting section, and it promises no more.
No consensus. One bucket key, one index, one clock. Two stores serving one bucket follow the same feed; two owners of one bucket are one owner with two machines.
Related standards
- OpenSwarm:
ipfilefor the swarms,pay2seedandpaid2seedfor holding and proof,ipdbfor the index,ippayfor the payee. - OpenDisk: the disks a store places onto, and their
holdinglists in which every object above appears. - OpenFile: a publisher's released files; a public bucket may be listed there, and an OpenFile entry may point at a bucket key.
- OpenSlice: the container that mounts a bucket.
- OpenServer: the storage offer a store maps onto, and the units this document borrows.
- OpenAccess: the token a client presents.
- OpenProfile.md: the owner and the operator.
Version history
| Version | Date | Change |
|---|---|---|
| 0.1 | 2026-09-21 | First publication: the store descriptor, the bucket, the object, placement, repair, the API, mounting, S3, the OpenServer mapping, c0mpute. |
License
The specification text is CC BY 4.0. Serve it, copy it, extend it.