# RFC-4 — CivicIPFS Directory and Conformance **Status:** Draft **Version:** 0.1 **Depends on:** RFC-1 (Addon Specification) v0.4, RFC-2 (Orchestrator Core) v0.5, RFC-3 (Civic Network Protocol) v0.3 **Author:** CivicIPFS Project **Repository:** TBD **Note:** This is the first version of this document. It resolves every open question left by RFC-1 §10/13, RFC-2 §16, and RFC-3 §13 that falls within directory/conformance scope. It does not modify any wire format, protocol mechanic, or enforcement behavior already specified in RFC-1, RFC-2, or RFC-3 — those remain entirely unchanged by this document. --- ## 1. Purpose and Scope RFC-1 through RFC-3 specify how the addon, orchestrator, and inter-orchestrator network *mechanically* operate. None of them specify who gets to participate, on what terms, or what makes a node trustworthy enough to be listed and discovered by others. That is the gap this document closes. RFC-4 covers: - The core distinction between *protocol conformance* and *network conformance* - The directory artifact — what it is, how it is published, and how it is not a service - The domain whitelist - Capacity-class conformance, including the canonical `capacity_class` vocabulary - The Steward and Witness roles, and a concrete, checkable separation test - Content/subject-matter filtering as a directory-time, disclosure-only concern - The content-type restriction's rationale (mechanism specified in RFC-2 §13.6) - Disclosure requirements for listing - The relationship between conformance and reputation (RFC-3) This document does not specify any JSON wire format already defined in RFC-1 or RFC-2 — it only specifies how existing fields are used for conformance evaluation. It does not specify WireGuard transport or Duniter attestation, which remain the separate future RFC referenced in RFC-3 §12. --- ## 2. Why This Document Exists — The Gap It Closes Across RFC-1, RFC-2, and RFC-3, the same pattern recurs: a mechanism is specified, and then explicitly **not** enforced by the software itself. - RFC-1 §3.1: Steward/Witness separation is declared by the channel, but "the addon does not enforce role separation." - RFC-2 §13.4: the orchestrator does not reject pins based on capacity-class arithmetic; "nothing happens" at the capacity boundary by design. - RFC-1 §9.3 / RFC-3 §2.1: there is no automatic orchestrator discovery and there will never be one. Each decision is correct on its own terms. Taken together, they leave open the question a participant actually cares about: "Is this node legitimate?" RFC-4 answers that question by locating it firmly outside the protocol layer — checked by humans or semi-automated tooling against a published artifact, not enforced by any API endpoint. ### 2.1 Protocol conformance vs. network conformance **Protocol conformance** is mechanical: does this orchestrator correctly implement the Subscription API (RFC-2 §8) and Civic Network API (RFC-3 §5)? Does it sign correctly? Does it respond to challenges (RFC-3 §6)? This is already fully covered by RFC-1 through RFC-3. **Network conformance** is the question this RFC answers: given that a node speaks the protocol correctly, should it be trusted, listed, and discoverable? A node can be perfectly protocol-conformant and fail network conformance — for example, an orchestrator that correctly implements every API endpoint but discloses `capacity_class: "home_lab"` while running enterprise-scale storage is protocol-conformant and not network-conformant. --- ## 3. The Directory Artifact ### 3.1 What it is The directory is a single, signed, machine-readable JSON document — not a service, not an API, not a database with a submission workflow. It is published by whoever curates it, at a URL of their choosing, the same way an orchestrator publishes its own `ServiceOffer` (RFC-2 §9.1). ```json { "schema": 1, "curator_id": "...", "curator_pubkey": "...", "published_at": 1700000000, "listed_orchestrators": [ { "orchestrator_id": "12D3KooW...", "orchestrator_url": "https://orchestrator.example.org", "capacity_class": "home_lab", "listed_at": 1700000000, "notes": "Verified 2026-06: capacity disclosure consistent with storage_total_gb." } ], "sig": "..." } ``` This is the minimal artifact consistent with the no-central-registry principle (RFC-3 §2.1): it is a *publication*, signed and falsifiable, not a service that can become a point of failure or a gatekeeper with infrastructure of its own. Anyone may publish a directory. Anyone may choose which directory, if any, to trust. The addon's orchestrator browser (RFC-1 §9.3) may optionally surface directory entries as a convenience, but the manual configuration field remains the only thing the addon actually requires — directory listings are advisory, never load-bearing. ### 3.2 How listing decisions are made There is no submission API, no automated approval pipeline, no specified workflow for this version. A curator evaluates a candidate orchestrator against the criteria in sections 4 through 7 of this document — by reading its `/api/v1/info` and `/api/v1/offers` responses (RFC-2 §8.3, §9), checking its `GET /civic/v1/pins` (RFC-3 §5) where relevant, and applying human judgment — then adds or omits it from their published directory. This is the same deliberate, manual, human-in-the-loop model RFC-1 §9.3 establishes for orchestrator selection generally, applied one layer up. --- ## 4. Capacity-Class Conformance ### 4.1 The principle A CivicIPFS Witness node's storage commitment should be sized so that running *another* node is the natural way to grow, rather than enlarging one node indefinitely. This mirrors the broader Civic Infrastructure project's pattern — Affected Status, Current Resident, HOA Homeowner, Property Taxpayer, Subject Expert, Resource Pool — each of which scales by adding more bounded, identity-bearing participants rather than by concentration. CivicIPFS applies the identical discipline to storage: the unit of growth is the **node**, not the disk. ### 4.2 The current figure: 64GB 64GB is the current reference figure for what counts as a "civic-class" Witness commitment per node. It is grounded in present-day commodity hardware economics — sized so a single SD card or small embedded drive is a complete, legitimate Witness node — not chosen arbitrarily. It is explicitly **not a protocol constant and not enforced anywhere in RFC-2's request-handling code** (RFC-2 §13.4 confirms no request-time arithmetic against this number). It is a directory-evaluation reference point only. **Revision process (resolves prior D3):** this figure is revised, when warranted, by republishing this document with an incremented version number. No formal trigger, cadence, or governance body is specified — consistent with the fact that no formal change-control process exists anywhere in RFC-1, RFC-2, or RFC-3 either. A curator who believes the figure is stale states so in their own published directory's notes; this document is updated when that view becomes settled, the same way every other revision in this project has happened. ### 4.3 The canonical `capacity_class` vocabulary (resolves prior Q3) RFC-2 §9.1 introduced `capacity_class` as a string with illustrative examples. This document makes those examples canonical and closed: | Value | Meaning | |---|---| | `home_lab` | Residential, small-office, or hobbyist-class hosting — the operator pays personally and the node runs on commodity or repurposed hardware. | | `datacenter` | Commercial hosting (VPS, dedicated server, cloud instance) — professionally operated infrastructure with a recurring monetary cost. | | `other` | Anything not cleanly described by the above two. **Requires `capacity_class_description` (RFC-2 §9.1) to be non-empty** — `other` with an empty description is not a valid disclosure and fails conformance on that basis alone. | No fourth value is defined in this version. A curator encountering a node that doesn't fit cleanly should use `other` with a clear description rather than wait for a vocabulary extension — extending this table, if ever needed, follows the same lightweight republication process as section 4.2. ### 4.4 What capacity-class conformance evaluates Given an orchestrator's self-disclosed `capacity_class` and `capacity_class_description` (RFC-2 §9.1), a directory curator considers: - Whether the disclosed and actual committed storage (`storage_total_gb`, RFC-2 §8.3) is consistent with the reference figure in section 4.2 - Whether the disclosure is *honest* rather than merely present — a node disclosing `home_lab` while running enterprise-scale storage fails conformance on dishonesty grounds, independent of the raw GB figure - That exceeding the reference figure is not itself disqualifying in any general sense — a node is simply not eligible for listing under the civic-class discipline. A large operator is free to run large infrastructure; they are asked to either disclose accurately and scale via multiple identity-bearing nodes, or to be honest that they are operating outside this discipline. ### 4.5 Per-Steward sub-allocation vs. node-level ceiling (resolves prior Q2) RFC-2 §9.1's per-tier `max_quota_gb` is **descriptive and advertised, not enforced** — the same status as `price_per_gb_month` (RFC-2 §9.1 already states prices are never validated or enforced). A curator may spot-check that the sum of an orchestrator's advertised tier quotas is consistent with its disclosed `capacity_class` and actual `storage_total_gb`, exactly as they would spot-check pricing claims for plausibility, but neither the orchestrator nor this document treats `max_quota_gb` as a binding ceiling enforced by anyone in software. It is a published claim, evaluated the same way every other disclosure in this document is evaluated. --- ## 5. Steward and Witness Separation RFC-1 §3.1 defines the Steward (originating publisher) and Witness (pinning infrastructure) roles and states separation is "a conformance property checked at the directory level (RFC-4)." This section is that check. ### 5.1 What separation means in practice A single operator running both a Steward channel and a Witness orchestrator for the *same content* on the *same node* defeats the purpose of redundancy — if that node disappears, both the origin and the only attestation of custody disappear together. ### 5.2 The separation test (resolves prior Q5/D2) A directory curator evaluating a candidate Witness orchestrator performs the following check, using only already-public data: 1. For a sample of CIDs in the orchestrator's published pin list (`GET /civic/v1/pins`, RFC-3 §5 — public, no authentication required) 2. Retrieve each pin's receipt (`channel_hash`, `orchestrator_id`, RFC-2 §10.1) 3. For each `channel_hash`, check whether that Hubzilla channel's *own configured/subscribed orchestrator* (as the channel itself would report, if reachable, or as inferable from the channel being the apparent Steward of that CID) shares the same `orchestrator_id` as the Witness being evaluated **Failure condition:** if a channel appears to be both the Steward of a CID (the originating publisher) and the CID's sole Witness via the same `orchestrator_id`, the separation test fails for that pin. A pattern of such failures across a sample is grounds for the curator to decline listing. This is deliberately a minimum bar, not an exhaustive test — it catches the clearest failure case (sole-Witness-equals-Steward's-own-node) using data the protocol already makes public, without requiring any new RFC-2 or RFC-3 endpoint. A more refined test (distinguishing "different node" from "different legal entity," for instance) is not specified in this version; the minimum bar is sufficient to be useful now and does not block listing decisions on a refinement that hasn't been designed. ### 5.3 Witness refusal is routine, not adversarial Per RFC-1 §3.1's employer/employee framing, a Witness declining to pin specific content is a normal infrastructure decision, not a political act. A curator should not treat a Witness's pattern of declines (per its disclosed filter lists, section 6) as evidence of non-conformance in itself. A Witness with a narrow, honestly-disclosed content policy is exactly as conformant as one with a broad policy. --- ## 6. Domain and Content Conformance ### 6.1 Domain whitelist (resolves prior D4) An orchestrator operator may restrict which Hubzilla hub domains may register against their node, beyond the realm-matching already specified in RFC-2 §8.2. This document does not standardize a format for such a whitelist, and does not require this document's directory artifact to record or distribute it. **This is intentional and permanent**, not a placeholder: domain whitelisting, where an operator chooses to apply it, is a private operational decision, consistent with the no-central-registry principle (RFC-3 §2.1). It is closed as out-of-scope-by-design rather than left pending. ### 6.2 Content filtering — disclosure-only, no server-side enforcement (resolves prior Q1/D6/D7) RFC-1 §6.5 and RFC-2 §8.2 specify the wire format for four filter lists (node allow/deny, channel allow/deny), reusing Hubzilla's existing `message_filter_incl`/`excl` syntax. RFC-2 §16 Q1 left open whether the orchestrator evaluates these against incoming pin requests. **This is now resolved: the orchestrator does not evaluate them.** RFC-2's `POST /api/v1/pin` (§8.3) is unaffected by this document and performs no filter-list check at any step. The four lists are **disclosure-only**: - A directory curator may surface a node's disclosed filter lists to help prospective subscribers judge fit before subscribing - A prospective subscriber may read a node's filter lists (via `/api/v1/info` or registration response, RFC-2 §8) before choosing to register - No party — addon, orchestrator, or directory — mechanically blocks a pin request based on these lists in this version This keeps RFC-2 entirely unchanged by this document and resolves the open question cleanly: filter lists inform human and curator judgment, nothing more, as of this version. This document does not define a controlled vocabulary for filter tags, consistent with RFC-1 §6.5's explicit choice to reuse Hubzilla's free-form syntax rather than impose one. --- ## 7. Content-Type Restriction — Rationale RFC-2 §13.6 specifies the mechanical enforcement of the text-only content restriction (two-signal byte sniffing, reject-on-ambiguity). This section records the rationale, which belongs here rather than in RFC-2: 1. **Liability transfer.** Both the Steward and the Witness assume legal liability for content they publish or pin. Restricting CivicIPFS to text-only content keeps that liability with the Steward and Witness, where it belongs by design, rather than letting it transfer to the Civic Infrastructure or the CivicIPFS project itself. 2. **Inviting alternative offerings.** The restriction deliberately leaves room for other operators to build less-restricted IPFS pinning services — for binary content, large media, or other use cases — outside CivicIPFS, rather than CivicIPFS attempting to be the only or universal pinning layer. Unlike capacity-class conformance, this restriction **is** enforced mechanically (RFC-2 §13.6), so it does not require a directory-level check — a node either correctly enforces it or it does not, and that is testable directly against the orchestrator's behavior, not evaluated by a curator's judgment. --- ## 8. Disclosure Requirements for Listing A node seeking to be listed should disclose, accurately, via the existing RFC-2 `/api/v1/info` and `/api/v1/offers` fields: - `capacity_class` (section 4.3) and `capacity_class_description`, honestly representing the node's actual operating context - Storage figures (`storage_total_gb`, etc.) consistent with the disclosed capacity class and the current reference figure (section 4.2) - Content filter lists (RFC-1 §6.5), where maintained, disclosed rather than hidden (section 6.2) - Reputation history (RFC-3 §7), where available — though per RFC-3 §7.4, a `null` score for a new or low-peer node is not itself a conformance failure Verification of truthfulness — as opposed to verification of signature validity, which RFC-2/RFC-3 already provide — remains an accountability matter for the disclosing party, evaluated by directory curators using the tests in sections 4 and 5, not by automated protocol enforcement. --- ## 9. Conformance and Reputation Are Permanently Separate (resolves prior D5) RFC-3's reputation score (§7.1) measures **technical reliability** — challenge pass/fail outcomes, nothing else. This document's conformance evaluation measures **policy and disclosure honesty** — capacity-class accuracy, Steward/Witness separation, content-type enforcement. **These are permanently separate and must never be merged into a single score or allowed to substitute for one another.** A technically reliable node (high reputation score) that dishonestly discloses its capacity class is not conformant, regardless of reputation. A perfectly conformant node that is new and has no challenge history has `null` reputation (RFC-3 §7.4) and is not thereby non-conformant. Conflating the two would let a technically-reliable-but-dishonest node launder its standing through reputation score, or let a conformant-but-new node be unfairly penalized by reputation's cold-start `null`. This separation is a permanent architectural rule, not subject to future revision without a clear, separately-argued reason. --- ## 10. The `declined` Pin Status — Deferred, Not Specified (resolves prior Q4) RFC-1 v0.4 introduced `declined` as a pin status the addon may display, anticipating a future mechanism for voluntary Witness refusal (section 5.3 of this document; RFC-1 §3.1's "Witness discretion"). **As of this version, no orchestrator behavior produces this status.** A `POST /api/v1/pin` request either succeeds or fails with a hard error (`realm_mismatch`, `storage_full`, `content_type_rejected` — RFC-2 §8.3, §13.6). There is no voluntary-refusal mechanism implemented anywhere in RFC-1 through RFC-4 as of this version. This is a deliberate deferral of the *feature*, not an unresolved question about an existing one: when a refusal mechanism is eventually designed (most likely as a future RFC-2 amendment, since it would need to be a request-time orchestrator behavior), `declined` is the status it should produce. Until then, the field exists in RFC-1's documented status enum but is currently unreachable in practice. --- ## 11. Deferred Items **D1 — Directory tooling beyond the artifact format.** Section 3 specifies the artifact format and that listing decisions are manual. Any tooling to generate, validate, or compare directory artifacts is left to implementation, not specified here. **D2 — Refinement of the Steward/Witness separation test.** Section 5.2 specifies a minimum-bar test using public data. A more refined test (e.g., distinguishing operator identity beyond `orchestrator_id`) is deferred. **D3 — Voluntary Witness refusal mechanism.** Section 10 defers the actual mechanism that would produce a `declined` status. This is a real, identifiable future RFC-2 amendment, deferred here only because it requires new request-time orchestrator behavior not yet designed. --- ## 12. What This RFC Deliberately Does Not Specify - Any wire format already defined in RFC-1 or RFC-2 — it only specifies how existing fields are used for conformance evaluation - Any protocol mechanics in RFC-3 (challenge issuance, byte-range selection, reputation scoring math) - WireGuard transport or Duniter attestation — remains the separate future RFC referenced in RFC-3 §12, distinct from this document - Payment mechanisms — permanently out of scope per RFC-1 §10/D5 and RFC-2 §3 --- *End of RFC-4 draft 0.1*