V1 Proposal (Archived)
The original permissions proposal, kept as the subject of the critique and superseded by the grants synthesis.

> Archived: this is the original proposal, kept so the critique has a stable subject. Its argument is unchanged, and only punctuation and phrasing were edited for style. The current design is Everything Is a Grant.

This is a design proposal for a new permissions system for Hypermedia content stored on IPFS. The goal is a versatile and simple privacy layer on top of a content-addressed blob store. Every blob is identified by its CID, and the permissions system decides who may read each blob from a given server.

Motivation

Today the daemon has a binary visibility model: a blob either has a blob_visibility row with space = 0 (public) or it does not (private). Visibility is propagated from structural blobs (Refs, Changes) down to the raw file blobs they link to, and a gateway running in public-only mode refuses to serve anything that isn't marked public. This works, but it has limits:

    Publishing does not carry an explicit, verifiable statement of intent. The server infers publicness from indexing rules, with no signed claim from the author.

    There is no way to grant read access to private content. Capabilities today (WRITER, AGENT) decide who may write into a space. Nothing decides who may read from it.

    Access decisions are server-local policy, and they cannot travel with the data. A peer that replicates blobs cannot verify what the author intended.

Design Overview

The system has three pillars:

    Signed publish envelopes: every publish is a signed statement about a CID, its timestamp, and its intended visibility.

    Read capabilities: delegable grants that allow an account (or key) to read specific content.

    Link-based access propagation: access to a blob extends to the blobs it links to, following the same DAG structure that visibility propagation uses today.

1. The Publish Envelope

The publish API must require a signature envelope wrapping every submitted blob or blob batch. The envelope is itself a small IPLD blob:

    cid: the CID of the new data being published (for a batch, the root CID).

    signer: the account (or a device key delegated by the account) making the claim.

    timestamp: when the publish happened. Gives ordering and enables later revocation/supersession semantics.

    visibility: an explicit claim: public or private (optionally scoped to a space).

    signature: over all of the above.

Properties this buys us:

    The server no longer infers publicness. It verifies a signed claim. A public-only gateway serves a blob if and only if it can produce a valid envelope marking it public (directly or via propagation, below).

    Envelopes are portable. Any replica can verify them offline. Publicness travels with the data, and no longer lives in one server's SQLite tables.

    The timestamp makes claims orderable: a later envelope from the same signer can change visibility (for example, unpublish), and replicas can converge on the latest claim.

Open question: is the envelope a new blob kind, or an extension of the existing Ref blob? Refs already sign a head CID and are the natural place for a visibility claim. A separate envelope kind would also cover non-document blobs (raw files pushed via the IPFS endpoints) that have no Ref.

2. Read Capabilities

Today capabilities delegate write roles (WRITER, AGENT) on a space. We extend the same machinery with read capabilities:

    A read cap is a signed blob: grantor (an account with authority over the content), grantee (an account or public key), subject (a CID, a document ID, or a space/path prefix), and optional constraints (expiry, no-redelegation).

    Presenting a valid read cap chain to a server authorizes serving the private blob(s) the cap covers. The existing authenticated-caller path in the blockstore (which already allows a caller with access to bypass public-only denial) becomes the enforcement point.

    Caps are delegable by default: a grantee can re-delegate a narrower cap, forming a chain back to the content owner. This is the same chain resolution the CLI already does for WRITER/AGENT caps when publishing into a shared space.

    Revocation follows the envelope timestamp model: a later signed statement by the grantor invalidates a cap. Servers enforce revocation at serve time. This is a policy layer with no cryptographic deletion: anyone who already fetched the bytes has them.

3. Access Propagation Through Links

The rule: if you are allowed to read a blob, you are allowed to read the blobs it links to, following the DAG downward through the same kinds of rules that drive visibility propagation today (Change → dep, Ref → head, anything → DagPB/Raw).

Rationale: a document is a DAG of many blobs: the Ref, the Changes, and the file and image blobs its content embeds. A read cap on "the document" must be usable without enumerating every constituent CID, and must keep working as the document changes. Granting on the root and propagating down mirrors how publicness already flows.

This is the part marked "I think??", and it deserves scrutiny:

    Direction matters. Propagation must be strictly downward (from the granted root into its dependencies), never upward or sideways. Being able to read a Change must not reveal other documents that happen to link to the same shared file blob.

    Shared blobs are fine. A raw file linked from both a public doc and a private doc is readable via the public path. That reveals nothing about the private doc's existence. Content-addressing already implies this: possession of a CID plus an authorized path to it is the access criterion.

    Rule-scoped. Propagation should follow the declared blob_visibility_rules-style patterns and ignore arbitrary IPLD links, so a maliciously crafted blob can't smuggle a link to someone else's private content and "launder" access to it. Propagation is evaluated against the owner's DAG: a cap on document D grants the blobs reachable from D's own version history, as signed by D's authors. It does not grant blobs that are merely referenced by CID from anywhere.

    Snapshot or living grant. A cap on a document ID follows new versions as they are published (read access to the document). A cap on a specific version CID grants only that frozen DAG. Both are useful; the subject field distinguishes them.

How the Pieces Fit

    Publish: client builds blobs, signs an envelope (CID + timestamp + visibility), submits both. The server verifies the signature before accepting, and indexes visibility from the claim instead of inferring it.

    Public read: unchanged fast path: gateway serves blobs whose envelope (or propagated envelope) says public, with public cache headers.

    Private read: caller authenticates (existing signed-request mechanism), presents or references a read cap chain. The server validates chain + revocations, then serves with private cache headers.

    Replication: peers exchange envelopes and caps alongside blobs. Every replica can enforce the same policy without trusting the origin server's database.

Principles

    Simple core: one envelope kind, one cap kind, one downward propagation rule. Everything else is composition.

    Verifiable as well as enforced: every access decision traces to signed statements, so any peer can make the same decision.

    Honest about limits: this controls what servers serve. It cannot control what past readers retain. Privacy of never-shared content is absolute (blobs unreferenced by any envelope are served to no one). Revocation is best-effort by design.

Open Questions

    Envelope as extended Ref, or a new blob kind (covers raw IPFS uploads with no Ref).

    Cap subject granularity: CID, document ID, or path prefix. Support all three, or start with document ID only?

    How does the upload flow change? Today POST /ipfs/file-upload stores blobs that are private-by-default with no signed statement at all. Should uploads require an envelope up front, or remain unclaimed until a document references them?

    Group or space-level read caps: grant to "all members of space X" instead of individual keys. Likely needed for team sites, but requires membership to be resolvable at serve time.

    Anonymous share links: a read cap granted to a bearer secret instead of a key, for "anyone with the link" sharing.

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime