Skip to content

Latest commit

 

History

History
331 lines (255 loc) · 19.2 KB

media-type.md

File metadata and controls

331 lines (255 loc) · 19.2 KB

The .rpack media type

Media type: application/vnd.forgeproof.rpack+json (vendor tree). Registration status: not yet submitted.

The line above is the only place this repository states where the registration stands. Every other surface — the format specification, the README, the changelog — uses the bare type name and points here.

The name identifies the outer .rpack document described by the normative specification, skills/run/references/rpack-format.md. It is one name for every revision whose format member is forgeproof-rpack: no version token, no parameters. It is never written into a bundle — format and version are the in-band identity — so it travels outside the file, for example in a Content-Type header. It does not describe the .sigstore.json sidecar (a Sigstore bundle), the .pub.pem sidecar (an SPKI PEM), or the chain file.

What a registration changes, and what it does not

  • Trust: nothing. RFC 6838 §4.10: "the registration of a media type does not imply endorsement, approval, or recommendation by the IANA or the IETF or even certification that the specification is adequate." A media type is a name. What a bundle proves, and what it does not, is stated in the security considerations below and is the same in every ledger state.
  • Verification: nothing. verify reads the format and version members and never the document's media type. No bundle byte changes when the ledger line does.
  • GitHub: nothing. raw.githubusercontent.com serves .rpack files as text/plain; charset=utf-8 whatever any registry says.
  • What it does give: a stable, collision-free name that tools can use for Content-Type, MIME databases, and editor associations.

The registration template

This is the RFC 6838 §5.6 template as it will be submitted through the IANA media types form. It is a draft until the ledger line says otherwise, and the ledger line — not this section — records what has happened to it.

Type name: application

Subtype name: vnd.forgeproof.rpack+json

Required parameters: N/A

Optional parameters: N/A

Encoding considerations: binary
   JSON text (RFC 8259). RFC 8259 Section 8.1 requires UTF-8 for
   interchange; the reference producer escapes all non-ASCII characters
   and therefore emits pure ASCII. Lines may exceed 998 octets (the
   embedded attestation carries a base64-encoded payload on one line),
   so the content is not 7bit or 8bit text. Line-ending convention (LF
   or CRLF), indentation, and member order are not significant; see
   Interoperability considerations.

Security considerations:
   This media type shares the security considerations of JSON (RFC
   8259, Section 12): consumers MUST NOT evaluate the content as code
   and MUST NOT rely on "eval()"-style parsing. Consumers SHOULD apply
   the implementation limits RFC 8259 Section 9 permits (size, nesting
   depth, number range) and SHOULD treat duplicate member names as an
   error or ensure that the verified and the displayed views of a
   document come from the same parse (RFC 8259 Section 4). The format
   carries no active content, scripts, macros, external references, or
   URLs that a conforming consumer must dereference, and employs no
   compression; the embedded attestation payload is base64-encoded
   text whose decoded size is bounded by the encoded size.

   A ForgeProof bundle is a tamper-EVIDENT provenance record, not a
   safety, security, review, or correctness attestation. It contains an
   Ed25519 signature over a SHA-256 digest of the document's canonical
   serialization; a valid signature proves only that the document was
   not altered after signing by the holder of the corresponding private
   key. It proves nothing about the correctness, safety, or quality of
   the code the document describes. Consumers MUST NOT treat a verified
   bundle as evidence that the described change was reviewed, tested to
   any standard, or secure, and MUST NOT present it as compliance with
   any regulation or framework.

   The verifying public key travels inside the document ("public_key").
   By default it is ephemeral, generated by the producing tool, deleted
   after signing, and bound to no identity, certificate authority, or
   transparency log: it is self-attested. A valid signature therefore
   proves that the document was not altered since signing, not who
   signed it. A future revision may carry identity material (for
   example a certificate and transparency-log entry) inside
   "attestation.verificationMaterial"; consumers MUST NOT assume it is
   present and MUST treat its contents as untrusted until verified by
   the rules of the Sigstore bundle format.

   Privacy: the document discloses the issue title and URL, repository-
   relative file paths, free-text design decisions, timestamps of the
   recorded session, a public-key comment that by default embeds the
   producing machine's "username@hostname", and (from format version
   1.1.0) approval records that may contain the approver's e-mail
   address. Producers publishing bundles SHOULD be aware they disclose
   these; consumers that display bundles SHOULD treat them as personal
   data. The producer's privacy policy is published with the format
   specification.

   Free-text members (issue title, requirement text, decisions, notes,
   approver identifiers) are untrusted input: consumers that render
   them as HTML or Markdown MUST escape them to prevent markup or
   script injection.

   The recorded artifact paths, and the issue number from which the
   chain file's name is built, are untrusted input too: nothing in a
   document prevents an artifact path that is absolute or that names a
   network share, nor either value from climbing out of the project with
   "..". Consumers that resolve them SHOULD confine resolution to the
   intended project root. The reference producer has refused to record
   such an artifact path, as the producing platform interprets it, since
   plugin version 1.1.0; documents from earlier versions may carry one.
   At the time of this registration the reference verifier does not
   confine them: it resolves each as given (on Windows a recorded
   network-share path is opened like any other path), so verifying a
   document from an untrusted source can make it open a file outside the
   project and disclose, through its verdict, whether a file exists
   there, whether an artifact path names a regular file, whether the
   file matches the digest recorded for it, and, for the chain file,
   whether it parses as JSON and, when it parses as an array of objects,
   its element count, the values of the members "index", "action" and
   "timestamp" of those objects, and the "commit_sha" in the "data"
   object of the last element whose "action" is "finalize", all of which
   the verdict echoes.

   The embedded attestation (format version 1.1.0 and later) is an
   in-toto Statement inside a DSSE envelope inside a Sigstore-style
   bundle object. It is data, not executable content. Its signature MUST
   be verified under the key carried in the document's own "public_key"
   member; a key or key hint carried inside the attestation itself is
   never a substitute for it. Its decoded payload is attacker-controlled
   JSON and MUST be parsed with the same limits as the outer document.

Interoperability considerations:
   The document is a single JSON object. Producers write UTF-8 without
   a byte-order mark (the reference producer writes ASCII). Member
   order, indentation, and newline convention are NOT normative:
   conforming verifiers re-serialize the parsed object in canonical
   form (members sorted by code point, no whitespace, non-ASCII escaped)
   before recomputing the digest, so consumers MUST NOT depend on byte
   layout. All numbers are integers. The format is append-only and
   versioned by the "version" member; consumers MUST ignore members
   they do not recognize and MUST NOT infer the presence of any member
   from the version value. The member "format" always has the value
   "forgeproof-rpack".

   Verifying the document's own signature requires OpenSSH "ssh-keygen"
   (8.1 or later, "ssh-keygen -Y verify") in addition to a JSON parser
   and SHA-256; the reference verifier is Python 3.11 standard library
   plus ssh-keygen and has no other dependencies. Verifying the embedded
   attestation requires only Ed25519 and JSON. Sigstore's cosign can
   independently check the DSSE signature of the attestation, as
   exported beside the document in the ".sigstore.json" sidecar file,
   against the exported public key (the ".pub.pem" sidecar file) and,
   for an artifact file supplied to it, that the file's digest is among
   the signed subjects. That complements and never replaces the
   verification the format specification defines, which additionally
   binds the attestation to the document's own "public_key", to its
   artifact list, and to its chain.

   From format version 1.1.0 the document may embed an attestation
   whose payload-type string "application/vnd.in-toto+json" (in-toto
   Attestation Framework) and wrapper media-type string
   "application/vnd.dev.sigstore.bundle.v0.3+json" (Sigstore bundle
   format) are conventions defined by those projects. Neither appears
   in the IANA media types registry at the time of this registration;
   this registration neither defines nor claims them.

Published specification:
   ForgeProof .rpack Bundle Format,
   https://github.com/ryanjmichie-git/forgeproof-plugin/blob/v1.4.0/skills/run/references/rpack-format.md
   (latest revision: the same path on the "main" branch).

Applications that use this media type:
   ForgeProof, a Claude Code plugin (producer and verifier), and the
   forgeproof-verify GitHub Action (verifier). Any JSON-aware tool can
   read the document.

Fragment identifier considerations:
   As specified for "+json" (RFC 6839, Section 3.1): no fragment
   identifier syntax is defined for "application/json", and this type
   defines none. JSON Pointer (RFC 6901) is not adopted.

Additional information:

   Deprecated alias names for this type: N/A
   Magic number(s): None at a fixed offset (JSON has none). The
      document begins with "{" (0x7B) and the reference producer emits
      the member "format" with the string value "forgeproof-rpack"
      within the first 256 octets; detection rules search that window
      for "\"format\": \"forgeproof-rpack\"" or
      "\"format\":\"forgeproof-rpack\"".
   File extension(s): .rpack
      (Note: ".rpack" is also used by unrelated binary game-asset
      archives whose first four octets are "RP5L" or "RP6L"; those files
      never begin with "{" and are distinguishable by content.)
   Macintosh file type code(s): N/A

Person & email address to contact for further information:
   Ryan Michie <ryanjmichie@gmail.com>

Intended usage: COMMON
   Interchanged publicly: committed to Git repositories and attached to
   pull requests.

Restrictions on usage: None

Author: Ryan Michie

Change controller: Ryan Michie (ForgeProof project),
   https://github.com/ryanjmichie-git/forgeproof-plugin

Provisional registration? (standards tree only): No

Detection files

Two definitions ship under share/ so that desktop and command-line tools can recognize a bundle. ForgeProof never installs them. No skill, hook, or engine command copies them anywhere, runs update-mime-database or xdg-mime, or writes to your home directory. They are there for you to install by hand, in your own user account, or not at all. CI proves both against every frozen fixture and a fresh bundle on each push (stress/check_detection.py), with the limits below asserted rather than hidden.

shared-mime-info: share/mime/packages/forgeproof-rpack.xml

For the desktop MIME database that file managers and GLib applications read. The definition is a sub-class of application/json, claims the *.rpack glob, and sniffs content: { at byte 0 and the format member within the first 256 bytes.

In the recipes below, <plugin-dir> is the directory the plugin was installed to (the marketplace cache or a checkout of this repository) and <your-project> is the repository whose bundle you are checking; neither share/ nor a bundle lives in the other. Install for your user only:

xdg-mime install --mode user <plugin-dir>/share/mime/packages/forgeproof-rpack.xml

which is equivalent to:

mkdir -p ~/.local/share/mime/packages
cp <plugin-dir>/share/mime/packages/forgeproof-rpack.xml ~/.local/share/mime/packages/
update-mime-database ~/.local/share/mime

Uninstall:

xdg-mime uninstall --mode user <plugin-dir>/share/mime/packages/forgeproof-rpack.xml

or delete ~/.local/share/mime/packages/forgeproof-rpack.xml and run update-mime-database ~/.local/share/mime again. System scope is the same with /usr/share/mime and root.

To try it without installing anything:

T=$(mktemp -d); mkdir -p "$T/mime/packages"
cp <plugin-dir>/share/mime/packages/forgeproof-rpack.xml "$T/mime/packages/"
XDG_DATA_HOME="$T" update-mime-database "$T/mime"
XDG_DATA_HOME="$T" gio info -a standard::content-type <your-project>/.forgeproof/issue-42.rpack

Never run update-mime-database on share/mime itself: it writes globs2, magic, mime.cache and more beside the XML. And query through gio info: on a session without a desktop environment, xdg-mime query filetype can fall through to file, which does not read this database.

What it reports (asserted in CI):

File Result
named *.rpack, any layout (as written, CRLF, compact, key-sorted, BOM-prefixed) the type above
no extension; as written, CRLF, or compact in the producer's member order the type above, by content
no extension; key-sorted or BOM-prefixed text/plain — a limit, see Known limits
an unrelated document named *.json application/json
a game archive's RP6L… bytes, no extension application/octet-stream

libmagic: share/magic/forgeproof

For file(1). There is nothing to install or uninstall: name the magic file on the command line.

file -e json -m <plugin-dir>/share/magic/forgeproof --mime-type <your-project>/.forgeproof/issue-42.rpack

-e json is required. file runs its built-in JSON detector before it consults any magic file and stops at the first match, so plain file --mime-type prints application/json for a bundle, with or without -m — for a bundle no larger than the prefix file inspects (its bytes parameter, at least 1 MiB by default; a bundle is a few kilobytes), beyond which the built-in detector gives up and the answer is text/plain without -m, or ours with it. That answer is correct, only less specific; it is asserted in CI so that the precedence is documented, not hidden. -m names an alternate magic list rather than adding to the default one, so with the command above most files that are not a bundle are reported as text/plain or application/octet-stream (file's other built-in detectors still run, so a CSV or a tar archive keeps its own type).

file -C -m writes <name>.mgc into the current directory; compile only in a temporary directory, never inside a checkout.

What it reports with -e json -m share/magic/forgeproof (asserted in CI):

File Result
as written, CRLF, or compact in the producer's member order the type above
key-sorted (indented or minified), or BOM-prefixed text/plain — a limit
unrelated JSON whose format is something else text/plain
a bundle as written, without -e json application/json — the built-in detector's precedence

For a future upstream submission

The shared-mime-info test-list line, recorded here for the merge request the checklist describes:

issue-996.rpack application/vnd.forgeproof.rpack+json

GitHub rendering

GitHub renders a .rpack as JSON, in the file view and in diffs, if the repository's .gitattributes carries this line:

*.rpack linguist-language=JSON

Optional and cosmetic: it changes how GitHub highlights the file; verification is unaffected either way, and no -text is needed for bundles.

Editor association

Optional and cosmetic: editors treat .rpack as plain text unless told otherwise. In VS Code, add to your user or workspace settings.json:

{
  "files.associations": {
    "*.rpack": "json"
  },
  "json.schemas": [
    {
      "fileMatch": ["*.rpack"],
      "url": "https://raw.githubusercontent.com/ryanjmichie-git/forgeproof-plugin/v1.4.0/schemas/forgeproof-rpack-1.schema.json"
    }
  ]
}

ForgeProof never writes editor settings, .gitattributes, or MIME definitions into your repository or your home directory; this snippet is for you to apply by hand, or not at all.

The .rpack extension

.rpack is also used by unrelated binary game-asset archives whose first four bytes are RP5L or RP6L. Those files never begin with {, so one byte of content tells the two apart; no registry assigns the extension to either format.

Known limits

  • A key-sorted re-serialization escapes content sniffing. Detection by content keys on the specification's emission profile: { at byte 0 and the format member within the first 256 bytes. A bundle re-serialized with sorted members (for example by a key-sorting JSON formatter) puts format behind the multi-kilobyte attestation; it remains valid and verifiable but is recognized by its file name only.
  • A byte-order mark escapes content sniffing too. The emission profile has producers write none, and a verifier ignores one; a BOM-prefixed copy is still valid but no longer has { at byte 0, so it is recognized by its file name only.
  • Plain file says application/json. Its built-in JSON detector runs before any magic file (on a bundle within the prefix file inspects — any realistic one); the rule under share/magic/ is reached only with -e json (see Detection files).
  • A game archive named .rpack is labelled by its name. On a desktop where a *.rpack glob has been installed, a file that fails the content test is still matched by the glob. The label is cosmetic: verify rejects anything that is not a ForgeProof bundle.

Apple platforms

Uniform Type Identifiers are declared in the Info.plist of an application bundle; there is no way to declare one without an app, and ForgeProof ships none. For any future macOS or iOS application the declaration would be com.forgeproof.rpack, conforming to public.json, with the .rpack extension and the media type above.

Upstream submission checklist

Submissions to upstream detection databases are maintainer actions, not release deliverables:

  1. Not before the ledger line above records a confirmed registration. shared-mime-info asks for "IANA registered mime-types when possible", and a definition submitted under a name that later changes needs an alias forever.
  2. shared-mime-info: a merge request carrying the XML definition and at least one test file ("Merge requests are required for new mime-types and should include one or more test files").
  3. file/libmagic: an entry with a one-line summary, the file extension, a contact, and a reference to the specification.
  4. Sign-off gate. The final text of every submission — and of every reply in its review — is presented to the project maintainer and sent only after his explicit sign-off.