<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.31 (Ruby 3.2.3) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dns-content-delivery-00" category="std" consensus="true" submissionType="IETF" xml:lang="en" version="3">
  <!-- xml2rfc v2v3 conversion 3.32.0 -->
  <front>
    <title abbrev="DNSC">DNS-Based Content Delivery &amp; Fallback Mechanism</title>
    <seriesInfo name="Internet-Draft" value="draft-dns-content-delivery-00"/>
    <author initials="A." surname="John" fullname="April Faye John">
      <organization/>
      <address>
        <postal>
          <country>DE</country>
        </postal>
        <email>aprl@sakamoto.pl</email>
      </address>
    </author>
    <date year="2026" month="March" day="26"/>
    <workgroup>Internet Engineering Task Force</workgroup>
    <abstract>
      <?line 18?>

<t>This document specifies a mechanism for serving content, such as HTML or JSON, directly via DNS TXT records. This feature is intended as a fallback mechanism when a primary service (A/AAAA record) is unreachable, or as a lightweight hosting solution for parked domains to display landing pages without requiring active HTTP servers or individual SSL certificates. Trust is established via DNSSEC, allowing browsers to treat the content as secure.</t>
    </abstract>
  </front>
  <middle>
    <?line 22?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>Managing HTTP servers and HTTPS certificates for a large number of parked domains or "placeholder" sites can be resource-intensive and operationally complex. Additionally, when a web server fails (connection refused or timeout), users are typically presented with a generic browser error page.</t>
      <t>This specification defines the "DNS Content" (DNSC) protocol, which allows User Agents (UAs) to retrieve content directly from the DNS system using structured TXT records. This feature is intended as a fallback mechanism when a primary service (A/AAAA record) is unreachable, or as a lightweight hosting solution for parked domains to display landing pages without requiring active HTTP servers or individual SSL certificates.</t>
    </section>
    <section anchor="terminology">
      <name>Terminology</name>
      <t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.</t>
      <t>This document makes use of terminology for record types (TXT), and other technical terms
   that are specific to the DNS.  Since these terms have
   specific meanings in the DNS, they are not expanded upon first use in
   this document.  For definitions of these and other terms, see
   [RFC8499].</t>
    </section>
    <section anchor="the-dnscontent-resource-record">
      <name>The _dnscontent Resource Record</name>
      <t>Content is published using DNS TXT records located at a specific prefix indicating the content type.</t>
      <section anchor="naming-convention">
        <name>Naming Convention</name>
        <t>The TXT record MUST be located at a label constructed as follows:</t>
        <artwork><![CDATA[
_<media-subtype>._dnscontent.<domain>
]]></artwork>
        <ul spacing="normal">
          <li>
            <t><tt>media-subtype</tt> corresponds to the IANA media type subtype (e.g., <tt>html</tt> for <tt>text/html</tt>, <tt>json</tt> for <tt>application/json</tt>).</t>
          </li>
          <li>
            <t><tt>domain</tt> corresponds to the FQDN of the service (e.g., <tt>example.com</tt>).</t>
          </li>
        </ul>
        <t><strong>Examples:</strong></t>
        <ul spacing="normal">
          <li>
            <t><tt>_html._dnscontent.example.com</tt> implies <tt>Content-Type: text/html</tt></t>
          </li>
          <li>
            <t><tt>_json._dnscontent.api.example.com</tt> implies <tt>Content-Type: application/json</tt></t>
          </li>
        </ul>
      </section>
      <section anchor="record-syntax-root-record">
        <name>Record Syntax (Root Record)</name>
        <t>The TXT record content is a semi-colon separated list of key-value pairs. The keys are case-insensitive.</t>
        <artwork><![CDATA[
v=DNSC1; [e=<encoding>;] [id=<stream-id>;] [tot=<total-chunks>;] c=<content>
]]></artwork>
        <table>
          <name>Parameters</name>
          <thead>
            <tr>
              <th align="left">Parameter</th>
              <th align="left">Name</th>
              <th align="left">Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td align="left">v</td>
              <td align="left">Version</td>
              <td align="left">REQUIRED. Must be <tt>DNSC1</tt>.</td>
            </tr>
            <tr>
              <td align="left">e</td>
              <td align="left">Encoding</td>
              <td align="left">OPTIONAL. Specifies the compression algorithm applied to the content <em>before</em> Base64 encoding.</td>
            </tr>
            <tr>
              <td align="left">id</td>
              <td align="left">Stream ID</td>
              <td align="left">OPTIONAL (REQUIRED if <tt>tot</tt> &gt; 1). A unique alphanumeric identifier (e.g., a short hash) for this version of the content. Used to correlate chunks.</td>
            </tr>
            <tr>
              <td align="left">tot</td>
              <td align="left">Total Chunks</td>
              <td align="left">OPTIONAL. Integer indicating the total number of records (chunks) required to reconstruct the content. Default is <tt>1</tt>.</td>
            </tr>
            <tr>
              <td align="left">c</td>
              <td align="left">Content</td>
              <td align="left">REQUIRED. The payload data encoded in <strong>Base64</strong>. If chunking is used, this field contains the <em>first</em> chunk (Sequence 0).</td>
            </tr>
          </tbody>
        </table>
        <t>The parameter <tt>e</tt> (Encoding) can have following the values:</t>
        <ul spacing="normal">
          <li>
            <t><tt>raw</tt> (Default): No compression.</t>
          </li>
          <li>
            <t><tt>gzip</tt>: GZIP compression [RFC1952].</t>
          </li>
          <li>
            <t><tt>br</tt>: Brotli compression [RFC7932].</t>
          </li>
        </ul>
      </section>
      <section anchor="chunking-mechanism">
        <name>Chunking Mechanism</name>
        <t>DNS messages have size constraints. To support content larger than a single TXT record's safe limit, the content MAY be split across multiple records.</t>
        <section anchor="chunk-discovery">
          <name>Chunk Discovery</name>
          <t>If <tt>tot</tt> &gt; 1 in the Root Record, the UA MUST fetch additional records. Subsequent chunks are located at subdomains prefixed with the sequence number (1-based index).</t>
          <artwork><![CDATA[
_<sequence-number>._<media-subtype>._dnscontent.<domain>
]]></artwork>
          <section anchor="example">
            <name>Example</name>
            <t>If Root is <tt>_html._dnscontent.example.com</tt>, then:</t>
            <ul spacing="normal">
              <li>
                <t>Chunk 1 is at <tt>_1._html._dnscontent.example.com</tt></t>
              </li>
              <li>
                <t>Chunk 2 is at <tt>_2._html._dnscontent.example.com</tt></t>
              </li>
              <li>
                <t>...up to <tt>tot - 1</tt>.</t>
              </li>
            </ul>
          </section>
        </section>
        <section anchor="chunk-record-syntax">
          <name>Chunk Record Syntax</name>
          <artwork><![CDATA[
v=DNSC1; id=<stream-id>; c=<content>
]]></artwork>
          <ul spacing="normal">
            <li>
              <t><strong>v</strong>: Must be <tt>DNSC1</tt>.</t>
            </li>
            <li>
              <t><strong>id</strong>: MUST match the <tt>id</tt> provided in the Root Record. UAs MUST discard chunks with mismatched IDs to prevent mixing versions during updates.</t>
            </li>
            <li>
              <t><strong>c</strong>: The Base64 encoded payload for this segment.</t>
            </li>
          </ul>
        </section>
      </section>
    </section>
    <section anchor="client-behavior">
      <name>Client Behavior</name>
      <section anchor="trigger-conditions">
        <name>Trigger Conditions</name>
        <t>A UA SHOULD initiate a DNSC lookup under the following conditions:</t>
        <ol spacing="normal" type="1"><li>
            <t>The UA fails to fetch A or AAAA records for a specified domain (NXDOMAIN or NOERROR/NODATA).</t>
          </li>
          <li>
            <t>The UA fails to establish a TCP connection to the IP addresses resolved from A/AAAA records (e.g., Connection Refused, Timed Out).</t>
          </li>
          <li>
            <t>A mechanism (e.g., a specific URI scheme) explicitly requests a DNSC request.</t>
          </li>
        </ol>
      </section>
      <section anchor="processing-logic">
        <name>Processing Logic</name>
        <ol spacing="normal" type="1"><li>
            <t>The UA constructs the query name based on the desired content type (<tt>_html</tt> for web navigation).</t>
          </li>
          <li>
            <t>Query TXT for <tt>_&lt;media-subtype&gt;._dnscontent.&lt;domain&gt;</tt>.</t>
          </li>
          <li>
            <t>Parse the TXT record. If <tt>v</tt> is not <tt>DNSC1</tt>, ignore.</t>
          </li>
          <li>
            <t>Fetch Chunks (if needed):
            </t>
            <ul spacing="normal">
              <li>
                <t>If <tt>tot &gt; 1</tt>, loop from <tt>i = 1</tt> to <tt>tot - 1</tt>.</t>
              </li>
              <li>
                <t>Query TXT for <tt>_&lt;i&gt;._&lt;media-subtype&gt;._dnscontent.&lt;domain&gt;</tt>.</t>
              </li>
              <li>
                <t>Validate <tt>id</tt>.</t>
              </li>
              <li>
                <t>Concatenate the Base64 strings from <tt>Root</tt> then <tt>_1</tt>, <tt>_2</tt>, etc.</t>
              </li>
            </ul>
          </li>
          <li>
            <t>Decode:
            </t>
            <ul spacing="normal">
              <li>
                <t>Base64 Decode the assembled string.</t>
              </li>
              <li>
                <t>Decompress based on the <tt>e</tt> parameter (<tt>gzip</tt>, <tt>br</tt>, or none).</t>
              </li>
            </ul>
          </li>
          <li>
            <t>Display the content with the implied Content-Type.</t>
          </li>
        </ol>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <section anchor="trust-origin">
        <name>Trust &amp; Origin</name>
        <t>Since DNSC does not use TLS certificates (X.509), trust is established via DNSSEC.</t>
        <ul spacing="normal">
          <li>
            <t>If the domain is signed with DNSSEC and the chain of trust is validated by the UA (or the recursive resolver trusted by the UA), the content MUST be treated as a <strong>secure Context</strong>.
            </t>
            <ul spacing="normal">
              <li>
                <t>UAs MAY display a specific indicator (e.g., "Verified by DNSSEC").</t>
              </li>
            </ul>
          </li>
          <li>
            <t>If DNSSEC validation fails or the zone is unsigned, the content MUST be treated as an insecure Context (similar to plain HTTP).
            </t>
            <ul spacing="normal">
              <li>
                <t>Powerful features (Geolocation, Service Workers, etc.) MUST be disabled.</t>
              </li>
            </ul>
          </li>
        </ul>
      </section>
      <section anchor="mixed-content-subresources">
        <name>Mixed Content &amp; Subresources</name>
        <ul spacing="normal">
          <li>
            <t>Relative Links should be resolved relative to the domain root.</t>
          </li>
          <li>
            <t>External Resources:
            </t>
            <ul spacing="normal">
              <li>
                <t>In a secure Context (DNSSEC), resources (JS, CSS, Images) SHOULD be loaded over HTTPS or verified DNSC.</t>
              </li>
              <li>
                <t>In an insecure Context, standard Mixed Content blocking rules apply.</t>
              </li>
            </ul>
          </li>
        </ul>
      </section>
      <section anchor="denial-of-service">
        <name>Denial of Service</name>
        <t>To prevent loops or excessive DNS traffic:</t>
        <ul spacing="normal">
          <li>
            <t>UAs SHOULD enforce a limit on the maximum number of chunks (<tt>tot</tt>). A recommended limit is 16 chunks.</t>
          </li>
          <li>
            <t>UAs SHOULD implement caching for DNSC records respecting the TTL.</t>
          </li>
        </ul>
      </section>
    </section>
  </middle>
  <back>
    <?line 156?>

<section anchor="examples">
      <name>Examples</name>
      <section anchor="simple-landing-page-no-chunking">
        <name>Simple Landing Page (No Chunking)</name>
        <artwork><![CDATA[
_html._dnscontent.example.com.  IN TXT  "v=DNSC1; e=raw; c=PGgxPldlbGNvbWU8L2gxPg=="
]]></artwork>
        <t>Decodes to <tt>&lt;h1&gt;Welcome&lt;/h1&gt;</tt></t>
      </section>
      <section anchor="compressed-chunked-content">
        <name>Compressed &amp; Chunked Content</name>
        <t>Root Record:</t>
        <artwork><![CDATA[
_html._dnscontent.domain.com.   IN TXT  "v=DNSC1; e=br; id=req89; tot=2; c=G873..."
]]></artwork>
        <t>Chunk 1:</t>
        <artwork><![CDATA[
_1._html._dnscontent.domain.com. IN TXT  "v=DNSC1; id=req89; c=...B729"
]]></artwork>
        <t>The UA fetches both, verifies <tt>id=req89</tt> matches, concatenates the Base64 payloads, decodes, decompresses using Brotli, and renders.</t>
      </section>
    </section>
  </back>
  <!-- ##markdown-source:
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
wx0AAA==

-->

</rfc>
