Capsule — Hosting Pattern

Status: Descriptive, not normative. As of v0.3.4 (2026-05-21), two independent hosts (MinDev and htmlbin.dev) have converged on the shape described below without coordinating with each other or with the Capsule project. This document records that convention so future hosts can adopt it consciously rather than re-derive it. See RESEARCH.md F21 for the empirical writeup.

The Capsule format is hosting-agnostic by construction. A valid capsule works without any host — it travels as a .html file across email, AirDrop, USB, cloud storage, and a recipient's local disk. Hosts are optional: they exist to give a capsule a public URL with a stable identity and to attest, in their own voice, that the bytes they served match the bytes they were given. Nothing in the format requires a host; everything in the format is compatible with one.

This document is a description of what hosts that serve capsules well actually do. It is not a contract producers can rely on; it is a pattern recipients and host implementers can use.

The observed pattern

ElementWhat hosts doWhy it works
Short URL identityEach capsule gets a stable URL with a compact identifier — UUID (MinDev), random slug (htmlbin), or whatever the host's namespace prefers.Capsules need a public address that travels via copy-paste, in messages, in citations. UUIDs and slugs are both fine; the discipline is stable (the URL never moves) and compact (people can share it).
/raw byte-identical endpointA second path returns the exact bytes the host received, with no chrome, no modifications, no transformations. MinDev: /api/c/{uuid}/raw. htmlbin: /p/{slug}/raw.Recipients (and validators) need a way to fetch the canonical bytes for verification or re-distribution. The "primary" URL may add navigation chrome for browser viewing; the /raw URL is the audit path.
Minimal host chromeThe capsule's own content fills the viewport. The host's UI recedes — a small mark, a footer attribution, a side rail. It never modifies the body.The host serves the capsule; it doesn't reframe it. A recipient should be able to tell at a glance that what they're looking at is the capsule's own rendering, not the host's repackaging of it.
Honest authorship attributionThe host says, in its own voice, who uploaded the bytes and when — without claiming to be the author. htmlbin's footer: "content authored by the agent that uploaded it." MinDev's response headers: x-capsule-content-hash, x-capsule-uuid.Hosts have information producers don't have (upload time, IP, account) and lack information producers do have (generator identity, source provenance). Hosts attest to what they know; the capsule's manifest carries the rest. The two layers stay distinct.
No content mutationThe host serves the bytes byte-identically. No re-rendering, no minification, no injection, no analytics tags, no rewrites.The capsule's integrity hash (when present) is computed over the bytes the producer sealed. Any host modification breaks that hash and breaks the trust signal. A host that modifies bytes is no longer hosting the capsule; it's serving a derived object.

MinDev demonstrates this; htmlbin does not (yet). The pattern is small and additive:

HTTP/2 200
content-type: text/html; charset=utf-8
content-length: 13734133
x-capsule-content-hash: sha256:60282cbdad54708f630f516a3b4534fe1f1063ebe670d5c79f0ee2e70eaf0e36
x-capsule-uuid: 9357a933-7ce1-4061-9488-2ca61d81bded

What the headers mean:

These headers are the host attesting to what the host computed. They are not a signature from the original author — that requires the signing layer parked in Appendix E.6. What the headers do guarantee: if the host's x-capsule-content-hash matches what the recipient recomputes from the body, the host did not modify the bytes in transit.

A host that doesn't include these headers is still a valid host of the pattern — it just provides one less independent verification. A host that includes them and serves bytes that don't match earns the recipient's mistrust.

Visibility tiers as host-side policy (not a format concern)

Hosts make access-control decisions independent of the format. Capsule itself takes no position on whether a hosted artifact should be world-readable, organization-scoped, token-gated, or fully private — those are host policy choices, not format properties. A capsule that travels by email is "as accessible as the recipient's inbox"; the same capsule hosted on MinDev under an org visibility is reachable only to authenticated org members; the same capsule hosted on htmlbin is anonymous-publishable. The bytes don't change; the access policy does.

Hosts SHOULD document their visibility model clearly, because producers and recipients need to understand what gating applies. Common tiers observed across host implementations:

TierWho can fetchNotes
privateUploader + explicit ACL granteesDefault for most hosts
orgMembers of the uploading organization (typically Firebase- or SSO-authenticated)Common in industry-tied hosts
shared-by-tokenAnyone with a per-recipient share-token URLSupports revocation, audit, expiry, view-cap
publicAnonymous resolvableFunctionally equivalent to a long-lived share token with no revocation or audit; some hosts deliberately do not offer this tier

Worked example (2026-05-21): MinDev shipped a security-driven change that removed the public tier entirely, on the basis that a single field bypassing every downstream access control was a structural mismatch with the host's other layers (token-scoped delivery, fetch logging, watermarking). All previously-public capsules were migrated to org; producers attempting to push with visibility=public now receive a 400 with a clear message redirecting them to the share-token path. This is a host-side policy decision; the capsule format is unchanged, and the same capsules hosted elsewhere (htmlbin, self-hosted, AirDrop) remain anonymous-resolvable. The format/host split working as designed.

Implication for producers (especially compiler-kind producers that emit QR codes or other resolvable pointers): host-side policy can change without notice, and format artifacts should not bake in assumptions about resolution semantics. The Core spec's QR-code recommendation to encode urn:uuid:<uuid> (rather than a live URL) exists precisely so producers don't ship artifacts that silently break when host policy changes. See RESEARCH.md F23 for the empirical case that validated this choice (Mintel had been encoding live MinDev URLs in QR codes; every previously-printed map's QR became a 403 for anonymous scanners overnight when MinDev removed the public tier).

If a producer explicitly wants anonymous-resolvable QRs or links, the right path is to mint a share token at upload time and encode the share-URL (/api/c/share/<token> or equivalent), not the bare-UUID URL. The share-URL form is anonymous-resolvable by design and stays anonymous-resolvable across host policy changes; the bare-UUID URL is contingent on whatever visibility tier the host happens to support today.

Hosts vs. registries — the optional commitment layer (sketch)

The descriptive pattern above is what hosts that serve capsules well do. A subset of hosts may want to declare more — to commit, publicly, to keeping their behavior stable over time so producers can encode contract-protected URLs into artifacts (QR codes, links in printed materials, references in long-lived documents). The term for that committed subset is registry.

A host serves capsules. A registry is a host that commits to keeping serving them in a particular way.

This is opt-in. A host can choose to be just a host (no commitments beyond "the bytes go out the way they came in") or to declare itself a registry (signing onto a public compliance contract). The format takes no position; producers and recipients gain a signal they can act on.

Capsule Registry Compliance v1 — sketched contract

Rough proposed shape. A host that publicly declares Capsule Registry Compliance v1 commits to the following for the lifetime of v1 (with breaking changes requiring v2 + a migration period):

  1. Stable URL pattern. Capsules reachable at a URL of the form <host>/<prefix>/<uuid-or-slug>. Pattern doesn't change without a major version bump + redirect period of at least N months.
  2. /raw byte-identical endpoint at the URL + /raw. Content-Type: text/html; charset=utf-8. Never mutates the body.
  3. Visibility commitment is part of the contract. Whatever visibility tier a capsule is uploaded under is honored for the capsule's lifetime, OR migration is announced with at least N months of notice. Removing a visibility tier (or downgrading existing capsules out of it) without grandfathering is a breaking change requiring a major version bump.
  4. Host-attestation headers (x-capsule-content-hash, x-capsule-uuid) on every /raw response.
  5. Honest deprecation. Breaking changes get a public changelog entry + a deprecation window + a clear migration path. Surprise policy changes that break in-the-wild artifacts are explicitly out of compliance.
  6. Capsule immutability. The registry serves the bytes it received. No mutation, no silent re-rendering, no injection.

Where this lives operationally

A host that declares compliance v1 should publish a machine-readable statement at a well-known location — proposed shape:

GET https://<host>/.well-known/capsule-compliance.json

{
  "compliance_version": "1.0",
  "url_pattern": "/c/<uuid>",
  "raw_suffix": "/raw",
  "visibility_tiers_supported": ["private", "org", "shared-by-token"],
  "attestation_headers": ["x-capsule-content-hash", "x-capsule-uuid"],
  "deprecation_policy_url": "https://<host>/docs/deprecation-policy",
  "declared_at": "YYYY-MM-DD"
}

Producers can fetch this once at build time (or at producer-script setup) to confirm the target host has declared compliance. Recipients with tooling that cares about registry guarantees can fetch this to verify what they can rely on.

Compliance v0 vs. v1

The descriptive pattern earlier in this document (the convergent host-contract observed between MinDev and htmlbin) is roughly compliance v0 — what hosts have converged on without formal commitment. The compliance contract sketched here is what v1 would look like if a host wanted to make those conventions binding.

Difference:

Producer-side implication

A producer encoding URLs into capsule QR codes can safely do so when the destination host has declared registry compliance v1 or higher. For non-compliant hosts (or unknown hosts), the URN form remains the safe default — see F23 in RESEARCH.md for the empirical case that motivated this distinction. The Core spec's Rule 4 QR guidance defaults to URN precisely because the format itself has no signal about host commitments; producers with signal (because their target host has declared compliance) can make different defaults for their build scripts.

The Mintel/MinDev case in F23 happened because MinDev was operating as a v0 host (descriptive pattern only, no formal commitment). Encoding bare-UUID URLs against a v0 host is a calibrated guess. Encoding them against a v1 host is a calibrated bet against a published contract — qualitatively different.

Status

This compliance contract is sketched, not yet adopted by any host as of 2026-05-21. No host has published a /.well-known/capsule-compliance.json declaration at the time of writing. If MinDev, htmlbin, or another host wants to be the first declarant, the path is:

  1. Implement the well-known endpoint per the proposed shape above (or propose changes to the shape if v1 needs refinement before any host signs on).
  2. Publicly commit to the six contract points for the lifetime of v1.
  3. Update this section to record the adoption, with a link to the host's compliance declaration.

Until at least one host signs on, the contract is descriptive of a possible future, not a current standard. See F24 in RESEARCH.md for the broader synthesis of why the host-vs-registry distinction matters.

Anti-patterns — what hosts of capsules should not do

Anti-patternWhy it breaks the pattern
Modify the body (re-render, minify, inject scripts, add analytics)Breaks the integrity hash. The host is no longer serving the capsule; it's serving a derived object that lies about its provenance.
Fail to provide a byte-identical fetch pathRecipients can't verify what they received. The host becomes opaque.
Maximalist chrome that obscures the capsule's bodyThe recipient can't tell what's the capsule and what's the host. Authorship attribution gets muddled.
Attest to things the host doesn't know (e.g., "verified author" without a signing infrastructure)Hosts can attest only to what they can compute from the bytes they received and the metadata they collected at upload. Anything else is overclaim.
Require host-side execution to view the capsule (e.g., server-side rendering of the manifest, JS proxies)Capsules are supposed to work on file://. If the host's rendering is required, you've lost the format's portability.
Lock the capsule to the host (no /raw path, no download button, no way to leave)Capsule rule 10 — "cannot be unshared" — implies recipients can take the bytes with them. A host that prevents this breaks the format's contract with recipients, not with producers.

Two reference implementations

MinDev — private, single-tenant. Hosts capsules produced by Mintel, an industry-tied mining-intelligence system. Validates each capsule against the spec on upload; rejects on mismatch. Per-recipient share tokens (each token a watermark; bytes identical across recipients, access path distinct). Fetch logging (who, when, IP, UA, via which token). Lineage resolution via parents[]. Recedes UI to a small rail when displaying — the capsule fills the viewport. Documented as the canonical example of the hosting pattern in Appendix E.7 of the spec ("the MinDev pattern: hosting-platform auth gates control delivery; the capsule itself doesn't gate its internal contents").

htmlbin.dev — public, multi-tenant, agent-first. "API for agents to share HTML. One human auth step, then your agent publishes over HTTP." Cloudflare D1 + KV stack. Doesn't impose Capsule format discipline — accepts any self-contained HTML. Adds a small header ([htmlbin] mark) and footer (hosted by htmlbin — content authored by the agent that uploaded it). Exposes /p/{slug}/raw for byte-identical fetch. Documented in PRECEDENTS.md "Current voices in HTML-for-AI" and archived as a voice in voices/utsengar-htmlbin-2026.html.

The two have very different audiences (industry-tied vs. agent-public), very different feature sets (per-recipient tokens + ACLs vs. open-publish flow), and very different attestation depth (response-header content-hash vs. footer attribution only). They converged on the shape anyway. That convergence is what this document records.

How recipients verify a hosted capsule

# Fetch via the /raw endpoint (NOT the primary URL — the primary may include host chrome)
curl -O https://mindev.ca/api/c/9357a933-7ce1-4061-9488-2ca61d81bded/raw

# Validate locally
python3 compiler/validate.py raw

# Or, as of v0.3.4, point the validator directly at the URL — it fetches via /raw,
# captures the response headers, and cross-checks any x-capsule-* attestations against
# the body before running the standard 26 checks:
python3 compiler/validate.py https://mindev.ca/api/c/9357a933-7ce1-4061-9488-2ca61d81bded/raw

The validator's URL mode reports any x-capsule-content-hash and x-capsule-uuid headers the host included, cross-checks them against the body's manifest, and surfaces any mismatch — turning the host's attestation into a verifiable claim rather than a trust-me string.

Why this document is descriptive, not normative

Two implementations is convergence. It's not yet a standard. Formalizing the pattern as a normative spec would require more host implementations to agree on protocol particulars (header names, slug format conventions, error response shapes, token semantics) — and that needs outreach and adoption pressure the project hasn't yet sought.

The reasonable middle path: document the pattern as observed, point future host implementers at it, let third-party implementations either align voluntarily or diverge consciously. If a third or fourth independent implementation appears and stays compatible with this pattern, the case for normative codification gets stronger. Until then: descriptive.

Open questions

Adoption

If you're building a host that serves capsules, the minimal recommended shape:

  1. Identity: assign a stable short URL per capsule. UUID (from manifest.uuid) or slug is fine.
  2. Raw endpoint: expose /raw (or equivalent) that returns the byte-identical body, no chrome, Content-Type: text/html; charset=utf-8.
  3. Chrome: minimal. The capsule's body is the foreground; the host's UI is a small mark and footer attribution. Never inject into the body.
  4. Attestation (optional but recommended): include x-capsule-content-hash and x-capsule-uuid headers in responses to /raw. Compute the content hash per §9.1.1. Lets recipients verify your hosting integrity.
  5. No mutation: serve the bytes you received. If you need to change them, you're building a different kind of service.
  6. Honest authorship: if you know who uploaded, say so in your voice (footer, response header, whatever fits). Never claim to be the author.
  7. Discoverability (recommended): publish an /llms.txt at the host's root that indexes the capsules you serve. The llms.txt proposal (Jeremy Howard, fast.ai / Answer.AI, September 2024; adopted as a Chrome Lighthouse audit) is the emerging convention for "give LLMs a curated, machine-readable map of this site's content." A hosting layer that exposes capsules but is invisible to LLM crawlers wastes most of the discoverability upside; an llms.txt listing each hosted capsule (with title, short description, and the /raw URL) closes that gap with ~zero ongoing maintenance cost. The htmlcapsule project's own site has one as a reference shape.

That's the whole pattern. Two independent hosts arrived at it by 2026-05-21; the bar for the third should be lower.

About this page · manifest · exports

This is a sealed HTML Capsule per Core spec v0.3.0. Five required inline blocks, no network dependencies, integrity hash over data + manifest. The content above is rendered from spec/HOSTING.md by the deterministic compiler/build_md_capsules.py at the time of the last source change.

Loading manifest…