<?xml version="1.0" encoding="utf-8"?>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc version="3" ipr="trust200902" docName="draft-nam-ksg-core-00" submissionType="IETF" category="info" xml:lang="en" xmlns:xi="http://www.w3.org/2001/XInclude" indexInclude="true">

<front>
<title abbrev="KSG-Core">Keysingate Core: Channel Documents, Status Records and Anchored Time</title><seriesInfo value="draft-nam-ksg-core-00" stream="IETF" status="informational" name="Internet-Draft"></seriesInfo>
<author initials="Y." surname="Nam" fullname="Yevhenii Nam"><organization>Keysingate</organization><address><postal><street></street>
</postal><email>nam@keysingate.com</email>
<uri>https://github.com/keysingate</uri>
</address></author><date year="2026" month="October" day="1"></date>
<area>General</area>
<workgroup></workgroup>
<keyword>delegation</keyword>
<keyword>anchoring</keyword>
<keyword>JCS</keyword>
<keyword>canonicalization</keyword>
<keyword>post-quantum signatures</keyword>

<abstract>
<t>This document specifies the core of Keysingate: the canonical form and
signature envelope of its documents, the time format and the rule that gives a
signed object an upper time bound from public-network anchors, and the
documents of the issuance channel -- release, delegation, block allocation,
block closure, agent binding, container initiation, key inclusion, class
promotion -- together with the status record that tracks a container's life.
It is written so that two independent implementations produce the same bytes
and reach the same verdict. The work journal kept inside a container, and its
export, are specified in a companion document.</t>
</abstract>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>The work journal and its export are specified in <xref target="KSG-JOURNAL"></xref>.</t>
<t>An issuer hands out ranges of serial numbers; distributors pass parts of them
down a channel; a holder turns a serial into a container by binding an agent's
key and an artifact to it at one moment. Every step is a signed document, and a
third party verifies the whole chain -- from the issuer's release to the
container's identifier -- without contacting the issuer and without trusting
any operator's word about time.</t>
<t>This document fixes what such verification needs to agree on: bytes, time,
anchors, channel documents and status records. It does not specify how a
container is built as a program, how keys are split or stored, which networks
anchor, how they are read, or what the content of a journal page means.</t>
</section>

<section anchor="conventions-and-terminology"><name>Conventions and Terminology</name>
<t>The key words &quot;<bcp14>MUST</bcp14>&quot;, &quot;<bcp14>MUST NOT</bcp14>&quot;, &quot;<bcp14>REQUIRED</bcp14>&quot;, &quot;<bcp14>SHALL</bcp14>&quot;,
&quot;<bcp14>SHALL NOT</bcp14>&quot;, &quot;<bcp14>SHOULD</bcp14>&quot;, &quot;<bcp14>SHOULD NOT</bcp14>&quot;, &quot;<bcp14>RECOMMENDED</bcp14>&quot;,
&quot;<bcp14>NOT RECOMMENDED</bcp14>&quot;, &quot;<bcp14>MAY</bcp14>&quot;, and &quot;<bcp14>OPTIONAL</bcp14>&quot; in this document are to
be interpreted as described in BCP 14 <xref target="RFC2119"></xref> <xref target="RFC8174"></xref> when, and only
when, they appear in all capitals, as shown here.</t>

<dl spacing="compact">
<dt>Release:</dt>
<dd>A signed declaration of a range of serial numbers by an issuer (<tt>Emission</tt>).</dd>
<dt>Block:</dt>
<dd>A range of serials sold to one holder, with a class and a term
(<tt>BlockAllocation</tt>).</dd>
<dt>Container:</dt>
<dd>The object created on one serial at initiation; its identifier is derived
(Section &quot;Identifiers&quot;).</dd>
<dt>Anchor:</dt>
<dd>Evidence from a public network that a subject existed no later than a
moment.</dd>
<dt>Network reader:</dt>
<dd>A component outside the core that, given an anchor of a kind it knows,
confirms the anchor and returns the moment the network proves.</dd>
<dt>Offset:</dt>
<dd>Milliseconds since a container's birth (<tt>offset_ms</tt>); not a date.</dd>
</dl>
</section>

<section anchor="canonical-form-and-envelope"><name>Canonical Form and Envelope</name>

<section anchor="canonicalization"><name>Canonicalization</name>
<t>Signed structures are JSON canonicalized with JCS <xref target="RFC8785"></xref>. A signature is
computed over the UTF-8 bytes of the canonical form of the document <strong>without</strong>
its <tt>signatures</tt> member. An absent optional member is omitted; <tt>null</tt> and
absence are different documents.</t>
</section>

<section anchor="numbers"><name>Numbers</name>
<t>Every number in a signed document <bcp14>MUST</bcp14> be an integer in <tt>0 ... 2^53 - 1</tt>. A
document containing a fraction, a negative number or a larger integer <bcp14>MUST</bcp14> be
rejected before any signature is checked: only such numbers serialize to the
same bytes in every JCS implementation.</t>
</section>

<section anchor="required-members"><name>Required Members</name>
<t>Every signed document of this core <bcp14>MUST</bcp14> carry
<tt>&quot;@context&quot;: &quot;urn:keysingate:core:v3&quot;</tt>, <tt>&quot;type&quot;</tt> naming the document and
<tt>&quot;v&quot;: 3</tt>. A member not defined for the document type <bcp14>MUST</bcp14> cause rejection:
a document two implementations would read differently is accepted by
neither.</t>
</section>

<section anchor="document-hash"><name>Document Hash</name>
<t><tt>hash(doc) = SHA-256(JCS(doc without signatures))</tt>, written as a multihash:
the string <tt>1220</tt> followed by 64 lowercase hexadecimal digits. Uppercase
digits <bcp14>MUST</bcp14> be rejected.</t>
</section>
</section>

<section anchor="signatures"><name>Signatures</name>
<t><tt>signatures</tt> is an array of objects <tt>{kid, alg, value}</tt>; <tt>value</tt> is base64url
without padding <xref target="RFC4648"></xref>.</t>
<table>
<thead>
<tr>
<th><tt>alg</tt></th>
<th>Use</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>Ed25519</tt></td>
<td>required; strict verification <xref target="RFC8032"></xref>: non-canonical encodings and small-order keys are rejected</td>
</tr>

<tr>
<td><tt>ML-DSA-65</tt></td>
<td>optional <xref target="FIPS204"></xref>; only as a further element, never instead of Ed25519</td>
</tr>
</tbody>
</table><t>A verification profile names <tt>required_algs</tt> (default <tt>[Ed25519]</tt>),
<tt>min_anchors</tt> (an integer &gt;= 1, default 1) and optionally <tt>now</tt>. A document
passes only if <strong>every</strong> required algorithm is covered by a valid signature of
a key from the set given for that document. An unknown <tt>alg</tt>, a bad encoding,
a <tt>kid</tt> outside the set, or a signature that does not verify <bcp14>MUST</bcp14> cause
rejection -- a failing signature is a refusal, never a gap in coverage.</t>
</section>

<section anchor="time"><name>Time</name>

<section anchor="form"><name>Form</name>
<t>A timestamp is exactly <tt>YYYY-MM-DDTHH:MM:SS.mmmZ</tt>: UTC, three digits of
milliseconds, 24 characters <xref target="RFC3339"></xref>. Any other form -- no fraction, another
fraction length, an offset, a leap second <tt>:60</tt> -- <bcp14>MUST</bcp14> be rejected. With one
fixed form, the order of the strings is the order of the moments.</t>
</section>

<section anchor="comparison"><name>Comparison</name>
<t>Moments are compared as milliseconds since 1970-01-01T00:00:00Z, not as
strings. The calendar is checked: month 1-12, day within the month, leap
years.</t>
</section>

<section anchor="offsets"><name>Offsets</name>
<t>Inside a container, time is <tt>offset_ms</tt>. It <bcp14>MUST NOT</bcp14> be presented as a date.
Only the bounds carry evidential weight: below, the release's <tt>issued_at</tt>;
above, an anchor (Section &quot;Anchors and the Upper Bound&quot;).</t>
</section>
</section>

<section anchor="identifiers"><name>Identifiers</name>
<t>A party is identified by an opaque URI; the core does not interpret it.</t>
<t>The container identifier is derived at initiation and re-derived at
verification:</t>

<artwork><![CDATA[container = SHA-256( F("ksg:container-id:v1") || F(emission)
    || F(issued_at) || F(serial, 8-byte big-endian)
    || F(agent key) || F(artifact)
    || F(offset_ms, 8-byte big-endian) )
F(x) = (length of x, 4-byte big-endian) || x
]]></artwork>
<t>where <tt>emission</tt> is the release id, <tt>issued_at</tt> the release's timestamp
string, <tt>agent key</tt> the base64url key string of the client agent, <tt>artifact</tt>
the artifact URI. The result is written as a multihash. A <tt>ContainerInit</tt>
whose <tt>container</tt> differs from the derived value <bcp14>MUST</bcp14> be rejected: the
identifier is evidence, not a claim.</t>
</section>

<section anchor="anchors-and-the-upper-bound"><name>Anchors and the Upper Bound</name>

<section anchor="anchor"><name>Anchor</name>

<sourcecode type="json"><![CDATA[{ "type": "<network URI>", "subject": "1220...",
  "proof": "<base64url>", "anchored_at": "..." }
]]></sourcecode>
<t>The core does not parse <tt>proof</tt>. A network reader, selected by <tt>type</tt>,
confirms that <tt>subject</tt> is recorded in the network and returns the moment the
network proves. <tt>anchored_at</tt> is a statement, not evidence.</t>
</section>

<section anchor="the-bound-rule"><name>The Bound Rule</name>
<t>One procedure applies to everything that is anchored -- a journal seal, a
status record, a release:</t>

<ol spacing="compact">
<li>every anchor <bcp14>MUST</bcp14> have <tt>subject</tt> equal to the object's hash; otherwise
reject;</li>
<li>an anchor of a kind for which the verifier has a reader <bcp14>MUST</bcp14> be confirmed;
an unconfirmed one <bcp14>MUST</bcp14> cause rejection (a forgery is a refusal, not
&quot;not found&quot;);</li>
<li>an anchor of a kind without a reader is skipped;</li>
<li>at least <tt>min_anchors</tt> (&gt;= 1) anchors <bcp14>MUST</bcp14> be confirmed; with fewer, the
object has <strong>no</strong> upper bound -- there is no bound &quot;by an operator's word&quot;;</li>
<li>the bound is the <strong>earliest</strong> moment among the confirmed anchors.</li>
</ol>
</section>

<section anchor="networks"><name>Networks</name>
<t>Which network anchors and how it is read is outside this document. A network
profile <bcp14>MAY</bcp14> make an anchor blind -- visible to outsiders only as the fact of a
write; the core sees only <tt>subject</tt> and <tt>proof</tt>.</t>
</section>
</section>

<section anchor="channel-documents"><name>Channel Documents</name>
<t>All channel documents are major version 3 with the envelope of Section
&quot;Canonical Form and Envelope&quot;. Their JSON Schemas are published at
<tt>https://schema.keysingate.com/core/v2/</tt> (Appendix &quot;Schemas&quot;).</t>

<section anchor="release-emission"><name>Release (Emission)</name>
<t>Members: <tt>id</tt> (<tt>ksg:em:</tt> followed by digits), <tt>range {from, to}</tt>,
<tt>block_ttl_days</tt> (&gt;= 1), <tt>issuer</tt>, <tt>issued_at</tt>, <tt>keys</tt>, <tt>signatures</tt>.
Self-signed. Trust goes only to keys present <strong>both</strong> in the release <strong>and</strong> in
the verifier's own set of trusted issuer keys. <tt>issued_at</tt> is the lower time
bound of everything under the release.</t>
<t><strong>Lifetime.</strong> A block may be opened for <tt>base * (10 + d) / 10</tt> milliseconds,
where <tt>base = block_ttl_days * 86 400 000</tt> and <tt>d</tt> is the depth of the channel
the block passed through (0 for a block sold by the issuer). Integer
arithmetic, saturating. The result <bcp14>MUST NOT</bcp14> exceed 730 days
(63 072 000 000 ms) whatever the release declares. The allowance per level
compensates the time a block spends travelling down the channel; the ceiling
bounds how long unopened stock may wait.</t>
</section>

<section anchor="delegation"><name>Delegation</name>
<t>The right to allocate blocks inside a sub-range. Members: <tt>emission</tt>,
<tt>range</tt>, <tt>delegate</tt>, <tt>keys</tt>, <tt>depth</tt> (0-4), <tt>parent</tt> (hash of the delegation
above; absent at depth 0), <tt>delegated_at</tt>, <tt>term_ms</tt>, <tt>expires_at</tt>,
<tt>signatures</tt>.</t>

<ul spacing="compact">
<li><tt>term_ms</tt> <bcp14>MUST NOT</bcp14> exceed 180 days; <tt>expires_at</tt> <bcp14>MUST</bcp14> be later than
<tt>delegated_at</tt> and <bcp14>MUST NOT</bcp14> be later than the parent's;</li>
<li>the range <bcp14>MUST</bcp14> lie within the granting party's range;</li>
<li>one invalid link invalidates the whole chain;</li>
<li>with <tt>now</tt> in the profile, an expired or not yet started link <bcp14>MUST</bcp14> cause
rejection; without <tt>now</tt>, a report <bcp14>MUST NOT</bcp14> state that the authority was in
force.</li>
</ul>
</section>

<section anchor="block-allocation"><name>Block Allocation</name>
<t>Members: <tt>emission</tt>, <tt>block {from, to}</tt>, <tt>class</tt>, <tt>holder</tt>, <tt>allocated_at</tt>,
<tt>expires_at</tt>, <tt>prev_closure</tt> (hash of the previous block's closure; <bcp14>REQUIRED</bcp14>
from the holder's second block), <tt>signatures</tt>.</t>

<ul spacing="compact">
<li>size by class: <tt>heavy</tt> 1-1 000; <tt>light</tt> 10 000-100 000; <tt>bare</tt> unbounded;</li>
<li>a holder's blocks are sequential and do not overlap;</li>
<li>a new block is allowed only once the previous one is used to at least 80 %,
computed in integers (<tt>used * 5 &gt;= size * 4</tt>);</li>
<li>signed by a release key or by the key at the end of a delegation chain;</li>
<li>the term belongs to the block: initiation before <tt>expires_at</tt> takes a
container out of the term for good; after it, the container's status is 20;
serials never initiated reach status 10.</li>
</ul>
</section>

<section anchor="block-closure"><name>Block Closure</name>
<t>Members: <tt>emission</tt>, <tt>block</tt>, <tt>used_submitted</tt>, <tt>used_not_submitted</tt>,
<tt>cancelled</tt>, <tt>closed_at</tt>, <tt>signatures</tt> (the holder's). The three sets <bcp14>MUST</bcp14>
cover the block exactly, without overlap; for <tt>heavy</tt>, <tt>used_not_submitted</tt>
<bcp14>MUST</bcp14> be empty.</t>
</section>

<section anchor="agent-binding"><name>Agent Binding</name>
<t>Members: <tt>emission</tt>, <tt>block</tt>, <tt>agent</tt>, <tt>bound_at</tt>, <tt>signatures</tt>. A one-time
binding of a block to the holder's agent key; a second binding of the same
block is invalid.</t>
</section>

<section anchor="container-initiation"><name>Container Initiation</name>
<t>Members: <tt>emission</tt>, <tt>serial</tt>, <tt>binding</tt> (hash of the binding), <tt>agent</tt>,
<tt>artifact</tt>, <tt>offset_ms</tt>, <tt>container</tt>, <tt>signatures</tt>. The client agent's key and
the artifact are bound at one and the same moment. Signatures of the agent and
of a second party (the owner or an orchestrator) -- at least two distinct
parties. The serial <bcp14>MUST</bcp14> lie in the binding's block; the offset <bcp14>MUST NOT</bcp14> run
past the block's lifetime; <tt>container</tt> <bcp14>MUST</bcp14> equal the derived identifier.</t>
</section>

<section anchor="key-inclusion"><name>Key Inclusion</name>
<t>A key is never replaced; a new one is laid on top. Members: <tt>emission</tt>,
<tt>block</tt>, <tt>predecessor</tt> (hash of the binding for the first, of the previous link
after that), <tt>key</tt>, <tt>offset_ms</tt>, <tt>signatures</tt> (by the previous link's key). The
valid set is the binding's key and every key of the unbroken chain; all stay
valid. At most 1 024 links.</t>
</section>

<section anchor="class-promotion"><name>Class Promotion</name>
<t>Members: <tt>container</tt>, <tt>from</tt>, <tt>to</tt>, <tt>offset_ms</tt>, <tt>signatures</tt> (owner and
issuer, at least two). Only upward: <tt>bare</tt> &lt; <tt>light</tt> &lt; <tt>heavy</tt>.</t>
</section>
</section>

<section anchor="classes"><name>Classes</name>
<t>A class is a tariff and the form in which a container's journal opens, not an
anchoring mode for life:</t>
<table>
<thead>
<tr>
<th>Class</th>
<th>Journal opens as</th>
<th>May turn to anchored form</th>
<th>Anchors</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>heavy</tt></td>
<td>anchored (&quot;Pro&quot;)</td>
<td>--</td>
<td>every seal</td>
</tr>

<tr>
<td><tt>light</tt></td>
<td>unanchored (&quot;lite&quot;)</td>
<td>yes, by a form-change page</td>
<td>only while anchored</td>
</tr>

<tr>
<td><tt>bare</tt></td>
<td>unanchored</td>
<td>no</td>
<td>never</td>
</tr>
</tbody>
</table></section>

<section anchor="status-records"><name>Status Records</name>
<t>A container's life is a sequence of statuses:</t>
<table>
<thead>
<tr>
<th>Status</th>
<th>Meaning</th>
<th>Status</th>
<th>Meaning</th>
</tr>
</thead>

<tbody>
<tr>
<td>0</td>
<td>printed</td>
<td>12</td>
<td>opened voluntarily</td>
</tr>

<tr>
<td>1</td>
<td>block applied</td>
<td>20</td>
<td>initiated after the term</td>
</tr>

<tr>
<td>2</td>
<td>buyer's key applied</td>
<td>21</td>
<td>prescription passed</td>
</tr>

<tr>
<td>3</td>
<td>sub-agent's key applied</td>
<td>22</td>
<td>key lost</td>
</tr>

<tr>
<td>4</td>
<td>initiated</td>
<td>30</td>
<td>in dispute</td>
</tr>

<tr>
<td>5</td>
<td>in work</td>
<td>31</td>
<td>dispute proven</td>
</tr>

<tr>
<td>6</td>
<td>frozen</td>
<td>32</td>
<td>dispute unproven</td>
</tr>

<tr>
<td>7</td>
<td>exported</td>
<td>34</td>
<td>appealed</td>
</tr>

<tr>
<td>9</td>
<td>lost</td>
<td>35</td>
<td>settled</td>
</tr>

<tr>
<td>10</td>
<td>never activated</td>
<td>8, 33</td>
<td>reserved, no transitions</td>
</tr>
</tbody>
</table><t>Each change is a <tt>StatusRecord</tt>: <tt>number</tt> (0 for the printing, then +1),
<tt>status</tt> (after the change), <tt>event</tt> (absent in record 0), <tt>offset_ms</tt>,
<tt>prev</tt>, <tt>signatures</tt>. Record 0's <tt>prev</tt> is the section root:</t>

<artwork><![CDATA[root = SHA-256( F("ksg:status-section:v1") || F(emission)
    || F(serial, 8-byte big-endian) )
]]></artwork>
<t>-- the serial's address, known before the container exists. Every later record
names its predecessor's hash. A change is valid only if the transition table
allows it from the previous status (Appendix &quot;Transition Table&quot;); the table
is normative and is also published as test vector <tt>08_status_table</tt>.</t>
<t>A status record is anchored, and its anchor is read by the Bound Rule; without
a confirmed anchor a status has <strong>no</strong> time, and a report <bcp14>MUST</bcp14> say so.</t>
<t><strong>Status section export.</strong> The release, the serial and every record with its
anchors, in order. Not signed as a whole: each record is signed and anchored,
and the chain from the root holds the order. Verification repeats acceptance:
record 0 from the address, every next record through the transition table,
linked, signed, anchored; then every anchor by the Bound Rule.</t>
</section>

<section anchor="verification"><name>Verification</name>
<t>Input: the object (a journal export, per the companion document, or a status
section), the container identifier, the class or the channel it derives from,
the keys, a profile, network readers.</t>

<ol spacing="compact">
<li>limits (Section &quot;Limits&quot;) -- before parsing;</li>
<li>parsing: version 3, strict members, numbers, time;</li>
<li>channel, if presented: release -&gt; delegations -&gt; block -&gt; binding -&gt;
initiation -&gt; container identifier; a closure of the block, if attached,
<bcp14>MUST</bcp14> be of that block, signed by the holder, cover it without gaps or
repeats and <bcp14>MUST NOT</bcp14> cancel this container's serial; without it, the
report states that the completeness of the block is not established;</li>
<li>the object itself (companion document, or Section &quot;Status Records&quot;);</li>
<li>anchors by the Bound Rule;</li>
<li>the report.</li>
</ol>
<t>The report states accepted or rejected with a reason, and lists explicitly
what is <strong>not</strong> established. A rejection is never replaced by a weakened
result.</t>
</section>

<section anchor="versions"><name>Versions</name>
<t>Documents of this core are major version 3; an implementation of this core
issues and verifies only that version. Adding an optional member is a minor
change; changing the meaning or requiredness of a member is a new major
version.</t>
</section>

<section anchor="limits"><name>Limits</name>
<table>
<thead>
<tr>
<th>What</th>
<th>Limit</th>
</tr>
</thead>

<tbody>
<tr>
<td>core document, bytes</td>
<td>64 KiB</td>
</tr>

<tr>
<td>journal export, bytes</td>
<td>8 MiB</td>
</tr>

<tr>
<td>signatures per document</td>
<td>8</td>
</tr>

<tr>
<td>anchors per object</td>
<td>16</td>
</tr>

<tr>
<td>Merkle path, links</td>
<td>64</td>
</tr>

<tr>
<td>delegation links</td>
<td>5 (depths 0-4)</td>
</tr>

<tr>
<td>key inclusion links</td>
<td>1 024</td>
</tr>

<tr>
<td>status section records</td>
<td>1 024</td>
</tr>
</tbody>
</table><t>Exceeding a limit <bcp14>MUST</bcp14> cause rejection before any signature is checked: a
verifier does not take on unbounded work.</t>
</section>

<section anchor="extension-rule"><name>Extension Rule</name>
<t>Where a set can be accepted, do not choose: signatures, anchors and
identifiers are arrays and opaque URIs. New things are added as elements, not
by replacing a member. An extension <bcp14>MUST NOT</bcp14> change the meaning or
requiredness of a core member.</t>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>
<t><strong>Time.</strong> The only upper bound is a confirmed anchor. An implementation that
falls back to <tt>anchored_at</tt>, to a server clock or to any operator's statement
when no anchor is confirmed violates this document; Rule 4 exists to make that
fallback impossible to express.</t>
<t><strong>Fail-closed.</strong> Anything unread, unknown or over a limit is a rejection.
Strict member parsing and the integer rule exist because a document that two
implementations read differently is an attack surface, not a compatibility
issue.</t>
<t><strong>Channel trust.</strong> A release is self-signed; trust comes from the verifier's
own set of issuer keys, never from the document. A delegation chain is only as
valid as its weakest link, and without <tt>now</tt> no statement about authority in
force is made.</t>
<t><strong>Post-quantum.</strong> ML-DSA-65 is added as a further signature, never as a
replacement: an implementation without it still verifies Ed25519, and one
requiring it states so in <tt>required_algs</tt>.</t>
<t><strong>Implementation.</strong> Verification keeps no state between calls, and library
code of the core returns every error as a value rather than aborting.</t>
</section>

<section anchor="privacy-considerations"><name>Privacy Considerations</name>
<t>Party identifiers are opaque URIs; the core neither resolves nor requires
personal data. A container identifier is a hash and reveals the artifact only
to someone who already holds it. Anchors may be blind (Section &quot;Networks&quot;):
the network then shows only that something was written.</t>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document has no IANA actions. <tt>urn:keysingate:core:v3</tt> is used as an
opaque, byte-compared string; no URN namespace is registered by this
document.</t>
</section>

</middle>

<back>
<references><name>Normative References</name>
<reference anchor="FIPS204" target="https://doi.org/10.6028/NIST.FIPS.204">
  <front>
    <title>Module-Lattice-Based Digital Signature Standard</title>
    <author>
      <organization>National Institute of Standards and Technology</organization>
    </author>
    <date year="2024" month="August"></date>
  </front>
  <seriesInfo name="FIPS PUB" value="204"></seriesInfo>
</reference>
<reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
  <front>
    <title>Key words for use in RFCs to Indicate Requirement Levels</title>
    <author fullname="S. Bradner" initials="S." surname="Bradner"></author>
    <date year="1997" month="March"></date>
  </front>
  <seriesInfo name="BCP" value="14"></seriesInfo>
  <seriesInfo name="RFC" value="2119"></seriesInfo>
</reference>
<reference anchor="RFC4648" target="https://www.rfc-editor.org/info/rfc4648">
  <front>
    <title>The Base16, Base32, and Base64 Data Encodings</title>
    <author fullname="S. Josefsson" initials="S." surname="Josefsson"></author>
    <date year="2006" month="October"></date>
  </front>
  <seriesInfo name="RFC" value="4648"></seriesInfo>
  <seriesInfo name="DOI" value="10.17487/RFC4648"></seriesInfo>
</reference>
<reference anchor="RFC8032" target="https://www.rfc-editor.org/info/rfc8032">
  <front>
    <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title>
    <author fullname="S. Josefsson" initials="S." surname="Josefsson"></author>
    <author fullname="I. Liusvaara" initials="I." surname="Liusvaara"></author>
    <date year="2017" month="January"></date>
  </front>
  <seriesInfo name="RFC" value="8032"></seriesInfo>
</reference>
<reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
  <front>
    <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
    <author fullname="B. Leiba" initials="B." surname="Leiba"></author>
    <date year="2017" month="May"></date>
  </front>
  <seriesInfo name="BCP" value="14"></seriesInfo>
  <seriesInfo name="RFC" value="8174"></seriesInfo>
</reference>
<reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
  <front>
    <title>JSON Canonicalization Scheme (JCS)</title>
    <author fullname="A. Rundgren" initials="A." surname="Rundgren"></author>
    <author fullname="B. Jordan" initials="B." surname="Jordan"></author>
    <author fullname="S. Erdtman" initials="S." surname="Erdtman"></author>
    <date year="2020" month="June"></date>
  </front>
  <seriesInfo name="RFC" value="8785"></seriesInfo>
</reference>
</references>
<references><name>Informative References</name>
<reference anchor="KSG-JOURNAL" target="https://datatracker.ietf.org/doc/draft-nam-ksg-journal-export/">
  <front>
    <title>Keysingate Journal Export: Pages under a Portable Seal</title>
    <author fullname="Yevhenii Nam" initials="Y." surname="Nam"></author>
    <date year="2026" month="October"></date>
  </front>
  <seriesInfo name="Internet-Draft" value="draft-nam-ksg-journal-export-00"></seriesInfo>
</reference>
<reference anchor="RFC3339" target="https://www.rfc-editor.org/info/rfc3339">
  <front>
    <title>Date and Time on the Internet: Timestamps</title>
    <author fullname="G. Klyne" initials="G." surname="Klyne"></author>
    <author fullname="C. Newman" initials="C." surname="Newman"></author>
    <date year="2002" month="July"></date>
  </front>
  <seriesInfo name="RFC" value="3339"></seriesInfo>
  <seriesInfo name="DOI" value="10.17487/RFC3339"></seriesInfo>
</reference>
<reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942">
  <front>
    <title>Improving Awareness of Running Code: The Implementation Status Section</title>
    <author fullname="Y. Sheffer" initials="Y." surname="Sheffer"></author>
    <author fullname="A. Farrel" initials="A." surname="Farrel"></author>
    <date year="2016" month="July"></date>
  </front>
  <seriesInfo name="RFC" value="7942"></seriesInfo>
  <seriesInfo name="DOI" value="10.17487/RFC7942"></seriesInfo>
</reference>
</references>

<section anchor="schemas"><name>Schemas</name>
<t>JSON Schemas of every document in Section &quot;Channel Documents&quot; and of the
status record are published at <tt>https://schema.keysingate.com/core/v2/</tt>
(<tt>common</tt>, <tt>emission</tt>, <tt>delegation</tt>, <tt>allocation</tt>, <tt>closure</tt>, <tt>binding</tt>,
<tt>container-init</tt>, <tt>key-inclusion</tt>, <tt>class-promotion</tt>, <tt>status-record</tt>). A
schema constrains shape, not rules: coverage, uniqueness, time bounds and
signatures lie outside it. An implementation that has checked only the schema
has not checked the document.</t>
</section>

<section anchor="transition-table"><name>Transition Table</name>
<t>Generated from the test vector <tt>08_status_table.json</tt>, which is
generated from the reference implementation. Every pair not listed is
refused. Events are those of the <tt>StatusRecord</tt> schema.</t>
<table>
<thead>
<tr>
<th>From</th>
<th>Event</th>
<th>Outcome</th>
</tr>
</thead>

<tbody>
<tr>
<td>0</td>
<td><tt>apply_packet</tt></td>
<td>-&gt; 1</td>
</tr>

<tr>
<td>1</td>
<td><tt>apply_buyer_key</tt></td>
<td>-&gt; 2</td>
</tr>

<tr>
<td>1</td>
<td><tt>initiate_too_late</tt></td>
<td>-&gt; 20</td>
</tr>

<tr>
<td>1</td>
<td><tt>packet_term_expired</tt></td>
<td>-&gt; 10</td>
</tr>

<tr>
<td>2</td>
<td><tt>apply_sub_agent_key</tt></td>
<td>-&gt; 3</td>
</tr>

<tr>
<td>2</td>
<td><tt>initiate</tt></td>
<td>-&gt; 4</td>
</tr>

<tr>
<td>2</td>
<td><tt>initiate_too_late</tt></td>
<td>-&gt; 20</td>
</tr>

<tr>
<td>2</td>
<td><tt>packet_term_expired</tt></td>
<td>-&gt; 10</td>
</tr>

<tr>
<td>3</td>
<td><tt>initiate</tt></td>
<td>-&gt; 4</td>
</tr>

<tr>
<td>3</td>
<td><tt>initiate_too_late</tt></td>
<td>-&gt; 20</td>
</tr>

<tr>
<td>3</td>
<td><tt>packet_term_expired</tt></td>
<td>-&gt; 10</td>
</tr>

<tr>
<td>4</td>
<td><tt>open_journal</tt></td>
<td>-&gt; 5</td>
</tr>

<tr>
<td>4</td>
<td><tt>declare_key_lost</tt></td>
<td>-&gt; 22</td>
</tr>

<tr>
<td>4</td>
<td><tt>declare_container_lost</tt></td>
<td>-&gt; 9</td>
</tr>

<tr>
<td>4</td>
<td><tt>serve_dispute</tt></td>
<td>-&gt; 30</td>
</tr>

<tr>
<td>5</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>5</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>5</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>5</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>5</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>5</td>
<td><tt>finalize</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>5</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>5</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>5</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>5</td>
<td><tt>declare_key_lost</tt></td>
<td>-&gt; 22</td>
</tr>

<tr>
<td>5</td>
<td><tt>declare_container_lost</tt></td>
<td>-&gt; 9</td>
</tr>

<tr>
<td>5</td>
<td><tt>serve_dispute</tt></td>
<td>-&gt; 30</td>
</tr>

<tr>
<td>6</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>6</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>6</td>
<td><tt>succeed</tt></td>
<td>stays</td>
</tr>

<tr>
<td>6</td>
<td><tt>declare_key_lost</tt></td>
<td>-&gt; 22</td>
</tr>

<tr>
<td>6</td>
<td><tt>declare_container_lost</tt></td>
<td>-&gt; 9</td>
</tr>

<tr>
<td>6</td>
<td><tt>serve_dispute</tt></td>
<td>-&gt; 30</td>
</tr>

<tr>
<td>12</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>12</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>12</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>12</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>12</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>12</td>
<td><tt>finalize</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>12</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>12</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>12</td>
<td><tt>declare_key_lost</tt></td>
<td>-&gt; 22</td>
</tr>

<tr>
<td>12</td>
<td><tt>declare_container_lost</tt></td>
<td>-&gt; 9</td>
</tr>

<tr>
<td>12</td>
<td><tt>serve_dispute</tt></td>
<td>-&gt; 30</td>
</tr>

<tr>
<td>30</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>30</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>30</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>30</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>30</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>30</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>30</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>30</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>30</td>
<td><tt>dispute_proven</tt></td>
<td>-&gt; 31</td>
</tr>

<tr>
<td>30</td>
<td><tt>dispute_unproven</tt></td>
<td>-&gt; 32</td>
</tr>

<tr>
<td>30</td>
<td><tt>extend_review</tt></td>
<td>stays</td>
</tr>

<tr>
<td>30</td>
<td><tt>review_deadline_passed</tt></td>
<td>blocks</td>
</tr>

<tr>
<td>30</td>
<td><tt>settle</tt></td>
<td>-&gt; 35</td>
</tr>

<tr>
<td>30</td>
<td><tt>prescription_passed</tt></td>
<td>-&gt; 21</td>
</tr>

<tr>
<td>31</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>31</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>31</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>31</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>31</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>31</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>31</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>31</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>31</td>
<td><tt>appeal</tt></td>
<td>-&gt; 34</td>
</tr>

<tr>
<td>32</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>32</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>32</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>32</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>32</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>32</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>32</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>32</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>34</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>34</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>34</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>34</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>34</td>
<td><tt>appeal_won</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>appeal_lost</tt></td>
<td>-&gt; 31</td>
</tr>

<tr>
<td>34</td>
<td><tt>extend_review</tt></td>
<td>stays</td>
</tr>

<tr>
<td>34</td>
<td><tt>review_deadline_passed</tt></td>
<td>blocks</td>
</tr>

<tr>
<td>34</td>
<td><tt>settle</tt></td>
<td>-&gt; 35</td>
</tr>

<tr>
<td>34</td>
<td><tt>prescription_passed</tt></td>
<td>-&gt; 21</td>
</tr>

<tr>
<td>35</td>
<td><tt>record_work</tt></td>
<td>stays</td>
</tr>

<tr>
<td>35</td>
<td><tt>record_artifact_or_rights</tt></td>
<td>stays</td>
</tr>

<tr>
<td>35</td>
<td><tt>transfer_ownership</tt></td>
<td>stays</td>
</tr>

<tr>
<td>35</td>
<td><tt>raise_class</tt></td>
<td>stays</td>
</tr>

<tr>
<td>35</td>
<td><tt>full_export</tt></td>
<td>-&gt; 7</td>
</tr>

<tr>
<td>35</td>
<td><tt>open_voluntarily</tt></td>
<td>-&gt; 12</td>
</tr>

<tr>
<td>35</td>
<td><tt>freeze_on_owner_application</tt></td>
<td>-&gt; 6</td>
</tr>

<tr>
<td>35</td>
<td><tt>succeed</tt></td>
<td>-&gt; 6</td>
</tr>
</tbody>
</table><t>While a container is blocked awaiting a decision, whatever its status:</t>
<table>
<thead>
<tr>
<th>Event</th>
<th>Outcome</th>
</tr>
</thead>

<tbody>
<tr>
<td><tt>dispute_proven</tt></td>
<td>-&gt; 31</td>
</tr>

<tr>
<td><tt>dispute_unproven</tt></td>
<td>-&gt; 32</td>
</tr>

<tr>
<td><tt>settle</tt></td>
<td>-&gt; 35</td>
</tr>

<tr>
<td><tt>prescription_passed</tt></td>
<td>-&gt; 21</td>
</tr>
</tbody>
</table><t>Terminal statuses: 7, 9, 10, 20, 22, 21. Working statuses: 5, 12, 30, 31, 32, 34, 35.</t>
</section>

<section anchor="implementation-status"><name>Implementation Status</name>
<t>Per <xref target="RFC7942"></xref>. <tt>ksg-core-v2</tt> (Rust) implements this document; with the
crates around it the workspace runs 752 tests; 13 test-vector sets are
regenerated and compared byte for byte by the reference build. The verifier <tt>ksg-verify-v2</tt> checks the
channel from release to container identifier and status sections with anchors
read through a Solana network reader.</t>
</section>

</back>

</rfc>
