From c4e12860f53debf3d4efea14552bfb39f8a53e26 Mon Sep 17 00:00:00 2001 From: Mixed Guffin <252040198+aXL333@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:07:03 +0930 Subject: [PATCH 1/5] Rename front page to PrismShare Code and credit author --- README.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 73e522c..b8e4b0c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# Prism Code +# PrismShare Code -Prism Code is an open colour 2D barcode. It stacks several ordinary QR Codes into -the red, green and blue channels of one symbol, so a single code carries several -times the data of the black and white QR Code it is built from, while staying -locatable by an ordinary QR detector. +**PrismShare Code** (**PS Code**) is an open colour 2D barcode. It stacks several +ordinary QR Codes into the red, green and blue channels of one symbol, so a +single code carries several times the data of the black and white QR Code it is +built from, while staying locatable by an ordinary QR detector. This repository is the **specification**, published for independent implementation and review. The reference implementation lives elsewhere; nothing @@ -15,7 +15,7 @@ Three layers, three documents. The symbol format knows nothing of what it carries; the payload protocols know nothing of the optics. - **[FORMAT.md](FORMAT.md)** - the normative symbol format: exactly what bytes - and colours one Prism Code contains. Read this to build an encoder or decoder. + and colours one PS Code contains. Read this to build an encoder or decoder. - **[DECODER.md](DECODER.md)** - the reference decoder, informative. How a reader recovers the payload, including calibration, bit-depth estimation, and matching the reader's rate to a sender it cannot talk back to. @@ -59,12 +59,18 @@ Corrections and review are welcome through this repository's issues. ## About the name -Prism Code is an independent open format. Every plane of a symbol is a QR Code, -and **QR Code is a registered trademark of DENSO WAVE INCORPORATED**; this -project is not endorsed by or affiliated with the trademark holder, and +PrismShare Code is an independent open format. Every plane of a symbol is a QR +Code, and **QR Code is a registered trademark of DENSO WAVE INCORPORATED**; +this project is not endorsed by or affiliated with the trademark holder, and implementers are responsible for their own assessment of any intellectual property that applies to QR Code encoding and decoding in their jurisdiction. +## Author + +PrismShare Code and its Aphotic file-transfer and Prism Stream payload protocols +were created and authored by **[aXL333](https://github.com/aXL333)** and are +published by **MASHR Systems**. + ## Acknowledgements The first public revision of these documents incorporates a detailed format From d9ba38a40189ae44bfd0198441e7443a4e0080d4 Mon Sep 17 00:00:00 2001 From: Mixed Guffin <252040198+aXL333@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:48:46 +0930 Subject: [PATCH 2/5] Polish PrismShare suite naming and conformance --- .github/workflows/spec.yml | 19 ++++ APHOTIC.md | 30 ++++-- BIT-LOADING.md | 12 ++- BRANDING.md | 96 +++++++++++++++++ CHANGELOG.md | 55 ++++++++++ CONFORMANCE.md | 81 ++++++++++++++ DECODER.md | 17 ++- EXAMPLES.md | 89 ++++++++++++++++ FORMAT.md | 39 ++++--- GLOSSARY.md | 31 ++++++ README.md | 175 ++++++++++++++++++------------ SECURITY.md | 75 +++++++++++++ STREAM.md | 61 +++++++---- VALIDATION.md | 65 ++++++++++++ VERSIONING.md | 55 ++++++++++ tools/check_spec.py | 177 +++++++++++++++++++++++++++++++ vectors/README.md | 37 +++++++ vectors/aphotic-fountain-v1.json | 26 +++++ vectors/manifest.json | 24 +++++ vectors/stream-v1.json | 68 ++++++++++++ vectors/symbol-vectors-v1.json | 14 +++ 21 files changed, 1129 insertions(+), 117 deletions(-) create mode 100644 .github/workflows/spec.yml create mode 100644 BRANDING.md create mode 100644 CHANGELOG.md create mode 100644 CONFORMANCE.md create mode 100644 EXAMPLES.md create mode 100644 GLOSSARY.md create mode 100644 SECURITY.md create mode 100644 VALIDATION.md create mode 100644 VERSIONING.md create mode 100644 tools/check_spec.py create mode 100644 vectors/README.md create mode 100644 vectors/aphotic-fountain-v1.json create mode 100644 vectors/manifest.json create mode 100644 vectors/stream-v1.json create mode 100644 vectors/symbol-vectors-v1.json diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml new file mode 100644 index 0000000..1103c93 --- /dev/null +++ b/.github/workflows/spec.yml @@ -0,0 +1,19 @@ +name: Specification + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Validate documents and vectors + run: python tools/check_spec.py diff --git a/APHOTIC.md b/APHOTIC.md index f50863b..fa1a74f 100644 --- a/APHOTIC.md +++ b/APHOTIC.md @@ -1,6 +1,8 @@ -# Aphotic +# Aphotic Transfer -Aphotic carries a file across a sequence of [Prism Code](FORMAT.md) symbols. A +**Aphotic Transfer** is PrismShare's exact-file payload protocol. Shortened to +**Aphotic** after first use, it carries a file across a sequence of +[PrismShare Code](FORMAT.md) symbols. A sender displays a file as an endless loop of symbols and a reader recovers it by watching; the link is one way, and Aphotic is what makes a one-way, lossy, camera-to-screen channel deliver an exact file anyway. @@ -8,12 +10,22 @@ camera-to-screen channel deliver an exact file anyway. It is a payload protocol: every page rides inside a Prism symbol's payload and Aphotic changes nothing about the symbol format. -**Status: implemented and in daily use at one bit per channel.** The wire format -below is what the reference sender emits and the reference reader accepts. +> **Document status** +> +> | field | value | +> |---|---| +> | role | normative exact-object payload protocol | +> | wire discriminator | `PS` (`0x50 0x53`) | +> | current revision | Aphotic Transfer version 1, identified by its fixed header and allocations | +> | repair modes | none, fixed parity, and Aphotic Fountain (`0xFF`) | +> | evidence | implemented and proven through real cameras at one bit per channel | + +The wire format below is what the reference sender emits and the reference +reader accepts. --- -## 1. What Aphotic is, and what it relies on +## 1. What Aphotic Transfer is, and what it relies on A file is split into fixed-size **chunks**. Each chunk, wrapped in a 13-byte **page header**, is one **page**, and one page is the payload of one symbol. The @@ -22,8 +34,8 @@ camera happens to catch, fills each chunk into its place, and has the file once every chunk is in hand. Aphotic delivers one exact object, eventually, taking as many laps as the losses -demand. That is the opposite of [Prism Stream](STREAM.md), which delivers a live -stream in real time and conceals what it cannot recover in time. A reader tells +demand. That is the opposite of [PrismShare Stream](STREAM.md), which delivers +a live stream in real time and conceals what it cannot recover in time. A reader tells the two apart, and both apart from a plain document, by the first two payload bytes: `PS` for an Aphotic page, `PV` for a stream frame, neither for a document. @@ -147,7 +159,7 @@ transfer, which nothing downstream would catch. > assembled file has no checksum. The bound is normative for exactly that > reason. -## 5. Rateless repair (the fountain) +## 5. Aphotic Fountain rateless repair Marked by a repair-mode byte of `0xFF`, which the fixed scheme can never emit, so the byte disambiguates the two on the receive side. @@ -333,7 +345,7 @@ does not run past the payload; and the name contains no control character and no That sequencing, loss handling and file identity are payload concerns that must live outside the optical symbol format, in a document of their own, was argued in a format review by **NomNomski**. This specification, and the -[Prism Stream](STREAM.md) protocol beside it, are that argument carried out. +[PrismShare Stream](STREAM.md) protocol beside it, are that argument carried out. ## License diff --git a/BIT-LOADING.md b/BIT-LOADING.md index 8c6891d..ab8b87d 100644 --- a/BIT-LOADING.md +++ b/BIT-LOADING.md @@ -1,4 +1,14 @@ -# Per-channel bit loading, a proposal for format version 3 +# PrismShare Code per-channel bit loading + +> **Document status** +> +> | field | value | +> |---|---| +> | role | experimental format-version-3 extension | +> | stable conformance target | no | +> | implementation | profiles defined; profile 1 implemented | +> | camera result | profile 1 falsified; remaining profiles unproven | +> | wire authority | `FORMAT.md` section 4 controls where the texts overlap | Status: implemented in the reference codec, and **the premise did not survive hardware**. Read the last section before building on any of this. Format diff --git a/BRANDING.md b/BRANDING.md new file mode 100644 index 0000000..b516766 --- /dev/null +++ b/BRANDING.md @@ -0,0 +1,96 @@ +# PrismShare naming and brand architecture + +> **Document status** +> +> | field | value | +> |---|---| +> | role | editorial naming authority | +> | wire effect | none | +> | suite | PrismShare Protocol Suite | +> | updated | 2026-08-12 | + +Names should make the system memorable without making the protocol obscure. +Every coined name therefore has a stable descriptive expansion. + +## 1. The hierarchy + +| name | kind | meaning | preferred first use | +|---|---|---|---| +| **PrismShare** | product and project | The applications and complete technology family. | “PrismShare, a one-way camera-to-screen sharing system” | +| **PrismShare Protocol Suite** | standard | The documents and vectors in this repository. | Use in repository, specification and conformance titles. | +| **PrismShare Code** | optical format | The colour 2D barcode defined by `FORMAT.md`. | “a PrismShare Code (PS Code) symbol” | +| **Aphotic Transfer** | payload protocol | Eventual, exact file delivery over `PS` pages. | “Aphotic Transfer, PrismShare's exact-file protocol” | +| **Aphotic Fountain** | repair mechanism | The deterministic rateless fountain inside Aphotic repair mode `0xFF`. | “the Aphotic Fountain rateless repair mechanism” | +| **PrismShare Stream** | payload protocol | Bounded-latency live delivery over `PV` frames. | “PrismShare Stream, the live redundancy protocol” | +| **Twin Window** | presentation technique | The informative two-symbol Stream layout carrying non-overlapping recent and older windows. | “the Twin Window two-symbol presentation” | + +## 2. Short forms + +After the first descriptive use in one document or screen: + +- “PrismShare Code” may become “PS Code” or “the symbol.” Do not shorten it to + “Prism Code”; the bare “Prism” form is deliberately avoided. +- “Aphotic Transfer” may become “Aphotic” or “the transfer.” +- “Aphotic Fountain” may become “the fountain.” +- “PrismShare Stream” may become “Stream” when it cannot be confused with a + generic byte stream. +- “Twin Window” retains both words; “twin” alone is not a protocol term. + +Source identifiers such as `Fountain`, `Transfer` and `VoiceStream`, and the +wire magics `PS` and `PV`, are compatibility artefacts. Editorial naming does +not require changing them. In prose, **PS Code** always means the optical +symbol; the wire value `PS` remains the Aphotic page magic and must not be +inferred from the abbreviation. + +## 3. Names not used as the umbrella + +- **Prism** alone is too crowded and too weakly searchable to be the product + identity. +- **Prism-spec** is a repository slug, not a user-facing name. +- **PrismAphotic** obscures the useful distinction between product and protocol; + use “PrismShare Aphotic Transfer” when the umbrella must be explicit. +- **Aphotic Fountain** names the repair mechanism, not the complete application + or the optical symbol. +- **Prism Stream** should be avoided in first-use prose because unrelated + streaming products use similar wording. Use “PrismShare Stream.” + +## 4. Naming new mechanisms + +A new coined name belongs in the suite only when it identifies a stable, +architecturally meaningful mechanism. It must: + +1. have a plain technical expansion; +2. name one thing at one layer; +3. avoid changing an existing wire meaning; +4. be added here and to `GLOSSARY.md` in the same change; and +5. survive a basic collision search before publication. + +Ordinary fields, constants and implementation tactics should remain descriptive. +The standard gains authority from precision; not every clever implementation +detail needs a brand. + +## 5. Repository and package naming + +Canonical and compatibility names: + +| surface | preferred value | +|---|---| +| specification repository | `Mashr-Systems/prismshare-spec` | +| application repository | `prismshare` | +| specification title | `PrismShare Protocol Suite` | +| app-store title | `PrismShare` plus a descriptive subtitle | +| command | `prism` may remain as a concise compatibility command | + +The specification repository moved from +`Blue-Heeler-Software/prism-spec` to `Mashr-Systems/prismshare-spec` on +2026-08-11. GitHub preserves the former address as a redirect. Package, signing +identity and app-store changes remain separate release operations. + +## 6. Clearance note + +This document is not a trade-mark opinion. “Prism” is crowded, and an unrelated +public GitHub repository already uses the exact string `PrismShare`. Ordinary +web and repository searches found no obvious use of “Aphotic Fountain” at the +time of writing, but absence from those searches is not legal clearance. +Professional searches should cover the intended jurisdictions and relevant +software, telecommunications and data-transfer classes before commercial use. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9dfe957 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# PrismShare Protocol Suite changelog + +Normative corrections are listed separately from editorial work so an +implementer can tell whether accepted bytes changed. + +## Unreleased + +### Editorial and conformance infrastructure + +- Establish **PrismShare** as the umbrella product and project name. +- Name the suite **PrismShare Protocol Suite**, the optical format + **PrismShare Code** with **PS Code** as its short form, the file protocol + **Aphotic Transfer**, its rateless + mechanism **Aphotic Fountain**, and the two-window presentation **Twin + Window**. +- Replace the ambiguous document-family introduction with an explicit layer + table and payload-dispatch table. +- Add naming, glossary, versioning, conformance, validation, security and worked + example companions. +- Seed machine-readable Aphotic and Stream vectors and document the remaining + full-symbol vector work. +- Remove the unnamed `margin = 6` geometry from the conformance surface while + retaining it as an explicitly experimental reader compatibility case. + +### Normative wire changes + +- None. Names and companion documents do not change any wire byte. + +### Normative clarifications + +- Define the structural checks possible before ignoring a Stream frame whose + codec id is unknown and therefore has no known packet size. + +## 2026-08-08 + +### Normative corrections + +- Require exact Aphotic page-count consistency. +- Require old Twin Window frames to clear Stream hint flags. +- Record strict unknown version/codec handling and Codec2 padding rules. + +### Evidence + +- Record PrismShare Stream results through real cameras and the measured link + behaviours that define the reference receiver. + +## 2026-08-06 + +- Publish Aphotic Transfer and PrismShare Stream as payload protocols separate + from the optical format. +- Credit the independent review that drove the separation. + +## 2026-08-06 (initial publication) + +- Publish the initial optical format and informative reference decoder. diff --git a/CONFORMANCE.md b/CONFORMANCE.md new file mode 100644 index 0000000..3cf8ae7 --- /dev/null +++ b/CONFORMANCE.md @@ -0,0 +1,81 @@ +# PrismShare conformance + +> **Document status:** normative conformance policy. It does not add wire +> fields or alter the protocol documents. + +Conformance is claimed per component and configuration. “PrismShare compatible” +without a component, version and tested configuration is not a complete claim. + +## 1. Claimable components + +| component | normative authority | minimum claim | +|---|---|---| +| PrismShare Code encoder | `FORMAT.md` | format version, loading/profile, QR version range, correction levels and layouts emitted | +| PrismShare Code decoder | `FORMAT.md` | format versions, loading profiles, QR version range and layouts accepted | +| Aphotic sender | `APHOTIC.md` | fixed repair modes and/or Aphotic Fountain mode supported, plus maximum payload and page count | +| Aphotic receiver | `APHOTIC.md` | accepted repair modes, resource limits and envelope handling | +| Stream sender | `STREAM.md` | profile version, codec ids, maximum packet count and flag behaviour | +| Stream receiver | `STREAM.md` | profile version, codec ids and invalid-frame dispositions | + +An implementation MAY claim any subset. It MUST NOT imply support for a layer or +configuration it has not tested. + +## 2. Conformance classes + +### Optical Core + +A conforming optical encoder emits the bytes, planes, geometry, calibration and +intensities required by `FORMAT.md`. A conforming optical decoder recovers the +normative payload from every positive vector in its claimed configuration and +rejects every corresponding negative vector. + +### Aphotic Transfer + +A conforming sender emits internally consistent headers, an exact systematic +pass and repair pages for its claimed modes. A conforming receiver reconstructs +the exact transfer payload from positive vectors and rejects malformed or +shape-changing pages as required by `VALIDATION.md`. + +### PrismShare Stream + +A conforming sender emits complete version 1 frames with one to 200 valid codec +packets. A conforming receiver accepts supported frames, ignores unsupported +codec frames, rejects malformed frames and applies wrap/session rules without +mixing incompatible state. + +## 3. Proof levels + +Conformance and physical usefulness are separate statements: + +| proof level | evidence | +|---|---| +| wire-conformant | Passes every applicable canonical positive and negative vector. | +| software-interoperable | Exchanges data with an independent implementation without sharing encoder internals. | +| camera-proven | Passes through a stated physical camera/display rig. | +| channel-proven | Survives a stated channel such as a video transcode, with the full configuration and result recorded. | + +“Camera-proven” MUST identify the format configuration and device class. It MUST +NOT be generalized to untested bit depths, versions, layouts or codecs. + +## 4. Present matrix + +| component | wire implementation | independent implementation | camera evidence | canonical vectors | +|---|---:|---:|---:|---:| +| Code v2, one bit/channel | yes | partial JavaScript encoder | yes | pending full symbol set | +| Code v2, two or three bits/channel | yes | partial | no | pending | +| Code v3 profile 1 | yes | no | falsified | pending | +| Aphotic fixed parity | yes | yes | yes | scaffolded | +| Aphotic Fountain | yes | yes | yes | seeded | +| Stream v1 AMR | yes | no | yes | seeded | +| Stream v1 Codec2 1300 | yes | no | yes | seeded | +| Stream v1 Codec2 700C | yes | no | limited | seeded | + +This table describes current evidence, not permanent protocol capability. + +## 5. Vector rule + +Files under `vectors/` are canonical only when their manifest entry has +`"status": "canonical"`. A scaffold or draft vector MUST NOT support a +conformance claim. Every canonical entry requires an origin description, +expected disposition and SHA-256 digest in `vectors/manifest.json`. + diff --git a/DECODER.md b/DECODER.md index 80d2dff..18adb46 100644 --- a/DECODER.md +++ b/DECODER.md @@ -1,8 +1,17 @@ -# Prism reference decoder +# PrismShare Code reference decoder -**Status: informative.** Nothing in this document is required for conformance. +> **Document status** +> +> | field | value | +> |---|---| +> | role | informative reference decoder | +> | conformance authority | `FORMAT.md`, not this document | +> | measured target | format version 2 at one bit per channel | +> | wire effect | none | -A Prism Code is defined by the bytes and colours in the symbol, which is +Nothing in this document is required for conformance. + +A PS Code is defined by the bytes and colours in the symbol, which is [FORMAT.md](FORMAT.md)'s job. How a reader recovers them is a quality-of- implementation question, and pinning one algorithm as normative would freeze today's calibration research into a permanent standard and forbid a better @@ -191,7 +200,7 @@ means the capture cannot carry even `b = 1` reliably, which in practice means th symbol is too small in frame, too blurred or too dark. This measures the channel, not the symbol being read, so a reader can be pointed -at any Prism Code and answer "how many bits per module will this setup carry". +at any PS Code and answer "how many bits per module will this setup carry". The number is tuned to under-promise: across the simulated capture sweep it never claims a depth that then fails to decode, and it frequently claims one less than what works. diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..458ebf2 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,89 @@ +# Worked wire examples + +> **Document status:** informative. Machine-readable values under `vectors/` +> are the canonical form when an example and a vector differ. + +Spaces in hexadecimal listings are for readability and are not transmitted. +All multi-byte integers are big-endian. + +## 1. Aphotic systematic page + +This example uses transfer id `19003` (`0x4A3B`), page index `0`, 79 data +pages, Aphotic Fountain mode `0xFF`, total payload length 5000 bytes and a +64-byte chunk. + +```text +50 53 | 4A 3B | 00 00 | 00 4F | FF | 00 00 13 88 + PS | id | index | pages |mode| length +``` + +The complete page is 77 bytes: the 13-byte header above followed by source +chunk 0. Since `ceil(5000 / 64) = 79`, the header is internally consistent. +Indices `0..78` are systematic pages; coded indices begin at 79. + +## 2. Aphotic Fountain coded page + +For the same transfer, coded index 79 derives neighbours `[13, 22]`. Its body +is `chunk[13] XOR chunk[22]`; the neighbour list is not transmitted. + +```text +50 53 | 4A 3B | 00 4F | 00 4F | FF | 00 00 13 88 +``` + +An independent receiver reconstructs `[13, 22]` using only transfer id, page +index and data-page count. The canonical body and further neighbour sets are in +`vectors/aphotic-fountain-v1.json`. + +## 3. PrismShare Stream AMR frame + +This version 1 frame uses session `0x1234`, codec id 0 (AMR-NB mode 0), no +flags, head sequence 2 and two packets. Each packet begins with the required +`0x04` TOC and contains a zero test payload. + +```text +50 56 | 01 | 12 34 | 00 | 00 00 02 | 02 + PV |ver |session|c/f | head | K + +04 00 00 00 00 00 00 00 00 00 00 00 00 +04 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +The packets carry sequences 2 and 1, newest first. The exact length is +`10 + 2 * 13 = 36` bytes. + +## 4. PrismShare Stream Codec2 frame + +This version 1 frame uses session `0xBEEF`, codec id 8 (Codec2 1300), the +talkspurt flag, head sequence `0x00FFFE` and one seven-byte packet. + +```text +50 56 | 01 | BE EF | 82 | 00 FF FE | 01 | 12 34 56 78 9A BC D0 +``` + +The final nibble is zero because Codec2 1300 uses 52 of the 56 carried bits. +A receiver ignores those four padding bits; a sender must transmit them as zero. + +## 5. Sequence wrap + +If a frame's head sequence is zero and `K = 2`, packet positions map to: + +| position | sequence | +|---:|---:| +| 0 | `0x000000` | +| 1 | `0xFFFFFF` | + +The subtraction is modulo `2^24`; it is not signed arithmetic. + +## 6. Invalid Stream frame + +```text +50 56 01 12 34 00 00 00 00 00 +``` + +This has a valid magic and version but `K = 0`. `STREAM.md` requires +`1 <= K <= 200`, so the frame must be rejected even though its ten-byte length +matches the empty count. + +Other negative examples, including an unknown profile and unknown codec, are in +`vectors/stream-v1.json` with their required dispositions. + diff --git a/FORMAT.md b/FORMAT.md index 224d145..c657642 100644 --- a/FORMAT.md +++ b/FORMAT.md @@ -1,11 +1,22 @@ -# Prism Code format specification +# PrismShare Code format specification -A Prism Code is a colour 2D barcode that stacks several ordinary QR Codes into +PrismShare Code, shortened to **PS Code** after first use, is a colour 2D +barcode that stacks several ordinary QR Codes into the red, green and blue channels of one symbol. Each stacked QR Code is a complete, standard, Reed-Solomon protected symbol, so all of the error correction work is done by a battle-tested implementation and Prism only has to get the colour right. +> **Document status** +> +> | field | value | +> |---|---| +> | role | normative optical wire specification | +> | stable target | format version 2 | +> | proven profile | version 2, one bit per channel | +> | experimental target | format version 3; see `BIT-LOADING.md` | +> | payload protocols | Aphotic Transfer (`PS`) and PrismShare Stream (`PV`) are specified separately | + This document is the **normative** format: what bytes and colours are present in a valid symbol. How a reader recovers them is described separately and informatively in [DECODER.md](DECODER.md); an alternative decoder conforms if it @@ -109,7 +120,7 @@ as described in RFC 2119. Text in blockquotes, and any passage that explains * **ISO/IEC 18004:2015**, *Information technology - Automatic identification and data capture techniques - QR Code bar code symbology specification.* Every - plane of a Prism Code is a QR Code Model 2 symbol as defined there, and this + plane of a PS Code is a QR Code Model 2 symbol as defined there, and this document does not restate its content. * **IETF RFC 2119**, key words for use in RFCs to indicate requirement levels. * **IEC 61966-2-1**, sRGB colour space, for the rendered intensity values. @@ -338,25 +349,20 @@ quietZone = margin - ringInset - ringThickness `quietZone` MUST be at least 1. Canvas size for a single matrix is `S = N + 2 * margin`. -Five layouts are currently accepted by the reference reader: +Four layouts are defined for conformance: | name | margin | ringInset | ringThickness | quiet zone | notes | |------|-------:|----------:|--------------:|-----------:|-------| | `COMPACT` | 4 | 1 | 1 | 2 | tightest; halved quiet zone | | `BALANCED` | 5 | 2 | 1 | 2 | halved quiet zone | -| *(unnamed)* | 6 | 2 | 1 | 3 | **see below** | | `STANDARD` | 7 | 2 | 1 | 4 | default, full QR quiet zone | | `THICK` | 8 | 2 | 2 | 4 | two module ring cells | `STANDARD` gives the QR specification's full 4 module quiet zone and is the -default. An encoder MUST use one of the accepted layouts. - -> The `margin = 6` entry has no name in the implementation: it appears only -> inside the reader's supported list, so a symbol built with it decodes while -> nothing can refer to it. Before this document is frozen that layout MUST -> either be named and specified like the others, or removed from the supported -> list. A standard cannot contain an anonymous accepted geometry, because two -> implementations cannot agree about a thing that has no name. +default. An encoder MUST use one of these four layouts. Some reference-reader +revisions also recognise an experimental `margin = 6` geometry, but it has no +standard name, is not a conforming encoder target, and is deliberately excluded +from decoder conformance claims until separately specified. > The tighter layouts are real density and they are measurably not free. Halving > the quiet zone makes a dense symbol at 6 pixels per module fail detection @@ -390,7 +396,7 @@ surround layouts. > A reader MAY additionally attempt ordinary QR decoding when no Prism header is > found, best-effort recovery from a cropped ring, or ringless recovery under > favourable conditions. Those are reader conveniences. A symbol without a ring -> is not a conforming Prism Code. +> is not a conforming PS Code. Let `a = ringInset`, `t = ringThickness`, and for a canvas of `W x H` modules: @@ -518,7 +524,7 @@ Two limits on that claim, both of which matter to an implementer: `STANDARD` surround, which carries the full 4 module quiet zone. The tighter layouts halve it, and a dense symbol at 6 pixels per module is measured to fail detection outright under a conventional binarizer. -* Locating a Prism Code is not the same as reading one. **A conventional QR +* Locating a PS Code is not the same as reading one. **A conventional QR payload decoder MUST NOT be expected to recover anything useful** from the composite luminance image, because the luminance of a coloured data module is not the black-or-white value any single plane holds. Prism is not payload @@ -933,7 +939,8 @@ Two such payload protocols are defined as their own documents: Neither touches this document. A reader tells the three payload kinds apart by the first bytes of the decoded payload: `PS` for an Aphotic transfer page, `PV` -for a Prism Stream frame, and neither for a plain document. +for a PrismShare Stream frame, and neither for a plain document. The complete +dispatch table is in the suite [README](README.md#payload-dispatch). Asymmetric bit depth beyond the defined profiles, luminance compatibility with ordinary QR payload decoders, and animated transfer are each a future format diff --git a/GLOSSARY.md b/GLOSSARY.md new file mode 100644 index 0000000..a7f55a8 --- /dev/null +++ b/GLOSSARY.md @@ -0,0 +1,31 @@ +# PrismShare glossary + +> **Document status:** editorial companion; no wire-format effect. + +| term | meaning | +|---|---| +| **application** | A PrismShare sender, receiver or combined user-facing implementation. | +| **Aphotic Fountain** | The deterministic, integer-only rateless repair mechanism selected by Aphotic repair mode `0xFF`. | +| **Aphotic Transfer** | The `PS` payload protocol that delivers one exact, finite object eventually. | +| **chunk** | One fixed-size slice of an Aphotic transfer payload, zero-padded only at the end. | +| **coded page** | An Aphotic repair page whose body is the XOR of a deterministic source-chunk subset. | +| **conformance vector** | Canonical input, expected output and disposition used to test an implementation. | +| **document** | A plain PrismShare Code payload that is neither an Aphotic page nor a Stream frame. | +| **frame** | One complete `PV` PrismShare Stream payload carried in one symbol. | +| **packet** | One fixed-size codec unit inside a Stream frame. | +| **page** | One complete `PS` Aphotic header and chunk body carried in one symbol. | +| **payload** | Bytes recovered from the optical symbol before optional payload-protocol interpretation. | +| **plane** | One complete monochrome QR Code stacked into a colour channel of a PrismShare Code symbol. | +| **profile** | A versioned selection of wire semantics, such as Stream v1's fixed-rate audio codec table. | +| **PrismShare** | The product, application family and project umbrella. | +| **PrismShare Code** | The colour optical symbol defined by `FORMAT.md`; “PS Code” is the accepted short form. The wire value `PS` separately identifies an Aphotic page. | +| **PrismShare Protocol Suite** | The normative specifications, informative companions and vectors in this repository. | +| **PrismShare Stream** | The `PV` live protocol that delivers time-sensitive packets with bounded redundancy rather than eventual completeness. | +| **receiver** | An implementation that decodes symbols and optionally interprets their payload protocol. | +| **repair page** | An Aphotic page sent after the systematic pass to recover missing chunks. | +| **session** | A 16-bit Stream identity whose change resets receiver state. | +| **symbol** | One rendered PrismShare Code matrix and its required surround. | +| **systematic page** | An Aphotic page below `dataPages` that carries its source chunk verbatim. | +| **transfer** | One Aphotic object identified by its transfer id and pinned header shape. | +| **Twin Window** | The informative two-symbol Stream presentation whose symbols carry non-overlapping recent and older redundancy windows. | +| **window** | The newest run of Stream packets repeated in each transmitted frame. | diff --git a/README.md b/README.md index 73e522c..2518141 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,119 @@ -# Prism Code - -Prism Code is an open colour 2D barcode. It stacks several ordinary QR Codes into -the red, green and blue channels of one symbol, so a single code carries several -times the data of the black and white QR Code it is built from, while staying -locatable by an ordinary QR detector. - -This repository is the **specification**, published for independent -implementation and review. The reference implementation lives elsewhere; nothing -here depends on it. - -## The family - -Three layers, three documents. The symbol format knows nothing of what it -carries; the payload protocols know nothing of the optics. - -- **[FORMAT.md](FORMAT.md)** - the normative symbol format: exactly what bytes - and colours one Prism Code contains. Read this to build an encoder or decoder. -- **[DECODER.md](DECODER.md)** - the reference decoder, informative. How a reader - recovers the payload, including calibration, bit-depth estimation, and matching - the reader's rate to a sender it cannot talk back to. -- **[BIT-LOADING.md](BIT-LOADING.md)** - per-channel bit loading, a format - extension whose first profile was implemented and falsified on real hardware; - the remaining profiles are implemented but untested. Kept so the dead end, and - the measurements that closed it, are not lost. - -Two payload protocols ride inside Prism symbols and are specified separately, so -they can never disturb the optical format: - -- **[APHOTIC.md](APHOTIC.md)** - a **file** across a sequence of symbols, with - rateless fountain repair so a reader that missed pages recovers without the - sender ever repeating itself. Complete-file, eventual delivery. -- **[STREAM.md](STREAM.md)** - a **live stream**, with a redundancy window and a - jitter buffer rather than a fountain, and an audio profile on top. Real-time - delivery that conceals what it cannot recover in time. - -Start with [FORMAT.md](FORMAT.md) section 0, which states plainly which -configurations are proven through a camera and which are only defined. +# PrismShare Protocol Suite + +PrismShare moves files and live media across a one-way camera-to-screen link. +It needs no pairing, network, account or return channel. The open specification +is a small family of layers rather than one monolithic format. + +**PrismShare** is the product and project name. **PrismShare Code** +(**PS Code**) is the colour 2D symbol at its foundation. **Aphotic Transfer** +carries exact files using the **Aphotic Fountain** for rateless repair. +**PrismShare Stream** carries live media with a bounded redundancy window. + +This repository is the authoritative **PrismShare Protocol Suite**. The +reference implementation lives elsewhere; conformance depends on the normative +documents and vectors here, not on reproducing its internals. + +> **Naming note.** The repository slug is an address, not the standard's name. +> The canonical repository is `Mashr-Systems/prismshare-spec`; GitHub preserves +> the former `Blue-Heeler-Software/prism-spec` address as a compatibility +> redirect. Prose and package listings use **PrismShare Protocol Suite**. See +> [BRANDING.md](BRANDING.md). + +## Architecture + +| layer or companion | document | status | purpose | +|---|---|---|---| +| optical symbol | **[FORMAT.md](FORMAT.md)** | normative | Bytes, colours, geometry and headers of a PrismShare Code symbol. | +| decoder guidance | **[DECODER.md](DECODER.md)** | informative | One measured way to recover a symbol through a camera. | +| optical experiment | **[BIT-LOADING.md](BIT-LOADING.md)** | experimental | Per-channel loading profiles, including the hardware result that falsified the first profile. | +| exact-object payload | **[APHOTIC.md](APHOTIC.md)** | normative | Aphotic Transfer pages and the Aphotic Fountain. | +| live payload | **[STREAM.md](STREAM.md)** | normative | PrismShare Stream frames, redundancy windows and the version 1 audio profile. | + +The optical format does not know what its payload means. The payload protocols +do not know how the symbol is displayed or photographed. This separation is a +conformance boundary, not merely an editorial arrangement. + +### Payload dispatch + +After a PrismShare Code symbol has yielded its payload bytes, the first two +bytes select the next layer: + +| prefix | payload family | required action | +|---|---|---| +| `PS` (`0x50 0x53`) | Aphotic Transfer | Parse as a 13-byte Aphotic page header followed by one chunk. | +| `PV` (`0x50 0x56`) | PrismShare Stream v1 | Parse as a 10-byte stream header followed by fixed-size packets. | +| neither | plain document | Deliver the payload to the application without interpreting it as either protocol. | + +`PV` does **not** mean “fountain stream.” Exact files and live media deliberately +use different loss disciplines: Aphotic uses rateless coded pages; Stream uses +recent-packet repetition and a playout buffer. + +## Conformance and implementation aids + +- [CONFORMANCE.md](CONFORMANCE.md) defines claimable components and the present + proof status. +- [VALIDATION.md](VALIDATION.md) collects required rejection and ignore rules. +- [EXAMPLES.md](EXAMPLES.md) provides worked wire examples. +- [GLOSSARY.md](GLOSSARY.md) fixes terminology used across the suite. +- [VERSIONING.md](VERSIONING.md) explains version fields, reserved values and + compatibility policy. +- [SECURITY.md](SECURITY.md) describes trust boundaries and resource limits. +- [vectors/](vectors/) contains canonical protocol vectors and the scaffold for + full symbol conformance vectors. +- [CHANGELOG.md](CHANGELOG.md) separates normative corrections from editorial + changes. ## Status This is a working specification, not yet a frozen standard. -- The symbol format at one bit per channel, and the Aphotic file transfer on top - of it, are proven on real handsets, including through a public video - platform's transcode: a paired version 6 H configuration was received by - every camera tested at every playback quality from 720p up. Section 0 of the - format records the measured envelope. -- Higher bit depths, and format version 3's per-channel loading, are defined but - have not decoded from a camera. Section 0 of the format is explicit about this. -- Prism Stream is verified end to end through real cameras: with Codec2 1300 - inside a version 8 symbol pair, the weakest handset tested streamed - contiguous speech from a monitor, and the strongest played minutes with no - packets concealed. STREAM.md section 8 has the measurements. -- Canonical conformance vectors do not exist yet. Until they do, no - implementation can demonstrate interoperability, and the specification says so. - -Corrections and review are welcome through this repository's issues. - -## About the name - -Prism Code is an independent open format. Every plane of a symbol is a QR Code, -and **QR Code is a registered trademark of DENSO WAVE INCORPORATED**; this -project is not endorsed by or affiliated with the trademark holder, and -implementers are responsible for their own assessment of any intellectual -property that applies to QR Code encoding and decoding in their jurisdiction. +- PrismShare Code format version 2 at one bit per channel is proven on real + handsets. A paired version 6 H presentation survived a public video + platform's transcode at every tested playback quality from 720p upward. +- Higher bit depths and format version 3's per-channel profiles are defined but + are not proven through a camera. Profile 1 was implemented and falsified. +- Aphotic Transfer and the Aphotic Fountain are proven on real handsets, + including through the same video transcode. +- PrismShare Stream v1 is verified end to end through real cameras. Codec2 1300 + inside a version 8 symbol pair carried contiguous speech on the weakest + handset tested. +- Canonical Aphotic and Stream protocol vectors now seed the conformance set. + Full symbol-image vectors remain a pre-freeze requirement; until they exist, + no implementation can demonstrate complete optical interoperability. + +## Branding and name clearance + +“Prism” is heavily used in software and broadcast products. The suite therefore +uses **PrismShare** as its primary compound mark and avoids presenting bare +“Prism” as the product name. **Aphotic Fountain** is the distinctive name of the +rateless repair mechanism; “fountain code” remains beside it as the searchable +technical description. + +This naming policy is product architecture, not legal clearance. Before a +commercial launch, obtain professional trade-mark searches in the intended +classes and countries and check app-store, package, domain and social handles. + +## About QR Code + +PrismShare is an independent open format. Every plane of a PrismShare Code +symbol is a QR Code, and **QR Code is a registered trademark of DENSO WAVE +INCORPORATED**. This project is not endorsed by or affiliated with the +trade-mark holder. Implementers are responsible for assessing any intellectual +property applicable to QR Code encoding and decoding in their jurisdiction. + +## Author + +PrismShare Code and its Aphotic Transfer and PrismShare Stream payload +protocols were created and authored by **[aXL333](https://github.com/aXL333)** +and are published by **MASHR Systems**. ## Acknowledgements -The first public revision of these documents incorporates a detailed format -review by **[NomNomski](https://github.com/NomNomski)**, whose change request -drove the split of the normative format from the reference decoder, the -correction of the capacity and calibration arithmetic, the honest accounting of -proven versus defined configurations, and the separation of the file-transfer and -streaming protocols into documents of their own. +The first public revision incorporates a detailed format review by +**[NomNomski](https://github.com/NomNomski)**. The review drove the split of the +normative format from the reference decoder, corrections to capacity and +calibration arithmetic, honest accounting of proven versus defined +configurations, and separate documents for file transfer and live streaming. ## License diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..bfb19bc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,75 @@ +# Security and trust boundaries + +> **Document status:** normative where it states required input handling; +> otherwise security guidance. PrismShare provides transport, not trust. + +## 1. No authentication + +PrismShare Code CRCs detect accidental damage with a residual collision +probability; they do not authenticate a sender. Aphotic Transfer and +PrismShare Stream add no signature, message authentication code or encryption. +Anyone who can place a readable symbol in the camera's view can offer content. + +Applications that require origin, integrity against an adversary or secrecy +MUST provide a signed, authenticated or encrypted object inside the payload. +The UI must not describe CRC acceptance as verification of the sender. + +## 2. Untrusted files and names + +An Aphotic filename is display metadata supplied by an untrusted sender. A +receiver MUST: + +- remove or neutralize directory separators and traversal components; +- refuse absolute paths and device names; +- choose the destination directory itself; +- avoid overwriting an existing file without an explicit local policy; and +- retain the complete payload as unnamed data when the envelope is malformed. + +Opening a received file invokes risks belonging to its file type. Automatic +execution, installation or active-document opening is outside the protocol and +SHOULD be disabled by default. + +## 3. Resource exhaustion + +Wire maxima are not allocation instructions. Validate lengths, counts and exact +shape before allocating or indexing. Receivers SHOULD publish and enforce +limits for: + +- maximum accepted payload bytes; +- concurrent Aphotic transfers; +- retained fountain equations per transfer; +- Stream packets and jitter depth; +- filename bytes; and +- time or symbol count spent on inactive state. + +A camera can repeatedly present valid but incomplete transfers. Eviction must +be deterministic and must not let a new sender inherit old state solely because +a 16-bit identifier collided. + +## 4. Identifier collisions and replay + +Aphotic transfer ids and Stream session ids are 16-bit correlation values, not +identities or nonces. Collisions are possible. Receivers pin all structural +fields for an active Aphotic transfer and reset Stream state on session or codec +change, but a deliberate same-shape collision remains possible. + +Recorded Stream content naturally reuses its session id and sequence numbers. +Replay handling prevents a receiver wedge; it does not establish freshness. +Applications that require freshness need an authenticated timestamp or nonce in +a future profile or inside the carried content. + +## 5. Privacy + +The optical channel is broadcast. Bystanders and other cameras can receive any +visible symbol. A display may also reveal filenames, progress, sender identity +or media context outside the encoded payload. Encrypt sensitive content before +sending and design user interfaces with shoulder-surfing in mind. + +## 6. External codecs and tools + +Media decoders, QR libraries and tools such as ffmpeg process attacker-controlled +input. Implementations SHOULD use maintained versions, argument arrays rather +than shell interpolation, bounded temporary storage and least-privilege process +execution. A protocol-conformant frame is not proof that its codec payload is +safe for a particular decoder. + diff --git a/STREAM.md b/STREAM.md index 4e39745..982f6f7 100644 --- a/STREAM.md +++ b/STREAM.md @@ -1,15 +1,29 @@ -# Prism Stream - -Prism Stream carries a live, real-time byte stream over a sequence of -[Prism Code](FORMAT.md) symbols. It is a payload protocol: it rides inside the -symbol payload and changes nothing about the symbol format. Audio is its first -and only defined profile, in section 5. - -**Status: implemented, unit-tested, and verified end to end through real -cameras.** The frame format and the receiver logic below are proven in software -against injected loss, and the audio profile has carried live speech from a -monitor to a fleet of handsets, including contiguous playback on the weakest -reader tested. Section 8 records what the optical link measured. +# PrismShare Stream + +**PrismShare Stream** carries live, time-sensitive packets over a sequence of +[PrismShare Code](FORMAT.md) symbols. It is a payload protocol: it rides inside +the symbol payload and changes nothing about the symbol format. + +Version 1 is deliberately narrower than a general-purpose streaming layer. It +defines fixed-size, fixed-duration codec packets and an audio codec table. It +does not define arbitrary fragmentation, per-packet lengths, timestamps, +negotiation or authentication. Those require a future profile version; they are +not accidentally missing fields in version 1. + +> **Document status** +> +> | field | value | +> |---|---| +> | role | normative live payload protocol with informative receiver guidance | +> | wire discriminator | `PV` (`0x50 0x56`) | +> | current profile | version 1, fixed-rate audio | +> | codecs | AMR-NB 4.75, Codec2 1300, Codec2 700C | +> | evidence | implemented, unit-tested and verified end to end through real cameras | + +The frame format is proven in software against injected loss, and the audio +profile has carried live speech from a monitor to a fleet of handsets, +including contiguous playback on the weakest reader tested. Section 8 records +what the optical link measured. --- @@ -22,8 +36,8 @@ it would rather conceal a gap than wait for a retransmission that would arrive too late to matter. A fountain peels a fixed object into completeness; a stream is unbounded and disposable. -So Prism Stream does not use the fountain. It shares only the symbol layer -beneath it, which gives it one property worth stating plainly: +So PrismShare Stream does not use the Aphotic Fountain. It shares only the +symbol layer beneath it, which gives it one property worth stating plainly: > A Prism symbol is Reed-Solomon protected and CRC-32 checked, so a decoded > frame is byte-exact or absent up to the strength of a 32-bit check: an @@ -57,7 +71,7 @@ recoverable outage = min(floor(W / P) - 1, floor(B / P)) lost symbols playout latency = B packets ``` -Deeper recovery buys itself with latency, one for one. This is why Prism Stream +Deeper recovery buys itself with latency, one for one. This is why PrismShare Stream is a broadcast and push-to-talk medium and not a two-way call, quite apart from the link being one-way. @@ -92,7 +106,7 @@ given codec, so no per-packet length is transmitted either. ## 4. Frame format -One Prism Stream frame is the byte payload of one symbol. All multi-byte fields +One PrismShare Stream frame is the byte payload of one symbol. All multi-byte fields are big-endian. | offset | size | field | @@ -108,16 +122,22 @@ are big-endian. The packet count `K` MUST be in the range 1 to 200; the upper bound is a defensive limit so a malformed count cannot make a reader allocate wildly. The frame length MUST be exactly `10 + K * packetBytes`; a payload that does not -account for every byte is not a Prism Stream frame. +account for every byte is not a PrismShare Stream frame. Unknowns are handled strictly: a payload naming an unknown profile version is -not a Prism Stream frame and MUST be rejected; a well-formed frame naming an +not a PrismShare Stream frame and MUST be rejected; a well-formed frame naming an unknown codec identifier MUST be ignored whole. The two undefined flag bits MUST be sent as zero and MUST be ignored on receipt, so a future revision can assign them without breaking receivers already in the field. A reader distinguishes a stream frame from an [Aphotic](APHOTIC.md) transfer page and a plain document by the magic: `PV` here, `PS` for a transfer page, neither for a document. +An unknown codec's packet size is necessarily unknown, so a version 1 receiver +cannot apply the codec-dependent exact-length equation to it. For this rule, +“well-formed” means that the ten-byte header is present, the profile version is +known and `K` is in `1..200`; the receiver then ignores the complete candidate +payload without slicing its packet area. + **Flags** (low nibble of byte 5): | bit | meaning | @@ -168,13 +188,14 @@ Measured end to end, that arithmetic is the whole story: the same 311 byte symbol payload that carries 460 ms of AMR redundancy carries 1.7 seconds of Codec2 1300, and section 8 shows what that difference buys. -> An audio-rate codec is not the only thing a Prism Stream could carry. The +> An audio-rate codec is not the only thing a future PrismShare Stream profile +> could carry. The > transport is codec-agnostic; a future profile could define a different codec > table, or a variable-rate codec, at which point a per-packet length and an > explicit timestamp, both omitted here because a fixed-rate codec needs > neither, would be added under a new profile version. -## 6. Two symbols, two windows (informative) +## 6. Twin Window: two symbols, two windows (informative) A sender with room for two symbols side by side, which is Prism's normal pair presentation, should not show the same frame twice, and should not show the diff --git a/VALIDATION.md b/VALIDATION.md new file mode 100644 index 0000000..2b56a89 --- /dev/null +++ b/VALIDATION.md @@ -0,0 +1,65 @@ +# Validation and disposition matrix + +> **Document status:** normative index of rules already owned by `FORMAT.md`, +> `APHOTIC.md` and `STREAM.md`. If this index conflicts with a protocol +> document, the protocol document controls and the index must be corrected. + +“Reject” means do not deliver the candidate as that protocol. “Ignore” means +the protocol was recognized but the complete unit contributes no receiver +state. Neither action permits partial bytes to enter an assembly or playout +buffer. + +## 1. Payload dispatch + +| condition | disposition | +|---|---| +| valid `PS` prefix | validate as Aphotic; never reinterpret a malformed Aphotic page as Stream | +| valid `PV` prefix | validate as Stream; never reinterpret a malformed Stream frame as Aphotic | +| neither prefix | deliver as a plain document if the application accepts documents | +| payload shorter than the selected protocol's header | reject as that protocol | + +## 2. Aphotic Transfer + +| condition | disposition | +|---|---| +| payload shorter than 13 bytes | reject | +| chunk size below 32 bytes | reject | +| total length zero, negative or high bit set | reject | +| `dataPages` is zero | reject | +| `dataPages != ceil(totalLength / chunkSize)` | reject | +| page index outside the legal range for its repair mode | reject | +| fixed parity would exceed index `0xFFFF` | sender MUST NOT emit | +| Aphotic Fountain has more than 60000 data pages | sender MUST NOT emit; receiver rejects an impossible shape | +| chunk size, page count, repair mode or total length changes for one transfer id | reject the conflicting page without poisoning retained state | +| duplicate systematic or coded page | MAY ignore; MUST NOT corrupt or duplicate output | +| malformed `PRNM` envelope | deliver the complete reassembled payload as unnamed data; never truncate it using an untrusted name length | + +## 3. PrismShare Stream v1 + +| condition | disposition | +|---|---| +| payload shorter than 10 bytes | reject as Stream | +| unknown profile version | reject as Stream | +| unknown codec id in version 1 | ignore the complete frame | +| packet count below 1 or above 200 | reject | +| frame length differs from `10 + K * packetBytes` | reject | +| undefined flag bits received | ignore those bits; do not reject an otherwise valid frame | +| undefined flag bits sent | non-conforming sender | +| AMR codec id with TOC other than the version 1 mode-0 form | reject the complete frame | +| non-zero Codec2 padding bits | receiver ignores padding; sender is non-conforming | +| session id changes | reset buffered stream state before accepting new packets | +| codec changes inside one session | reset buffered stream state | +| backward head jump greater than the implementation's replay threshold | treat as recorded-session restart; reference threshold is 600 packets | +| older Twin Window frame carries silence or talkspurt hint | non-conforming sender; receiver uses flags only at the frame's head | + +## 4. Resource discipline + +Before allocation, validate every transmitted count against the relevant +protocol ceiling and the implementation's declared local ceiling. Receivers MAY +apply stricter local limits, but SHOULD surface that policy rather than describe +a resource refusal as malformed wire data. + +An implementation processing multiple visible senders MUST bound concurrent +assemblies, retained coded equations, frame buffers and filenames. Eviction MUST +discard a complete transfer/session state; it MUST NOT merge partial state under +a recycled identifier. diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..47d99ac --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,55 @@ +# Versioning and extension policy + +> **Document status:** normative where it states receiver behaviour; otherwise +> editorial guidance for future revisions. + +## 1. Independent layers + +PrismShare Code, Aphotic Transfer and PrismShare Stream are independently +versioned protocol layers. A revision to one MUST NOT silently reinterpret +bytes owned by another. + +- PrismShare Code carries its format version in the symbol header. +- Aphotic version 1 has no separate version byte. Its magic, repair-mode values + and exact header shape define the current protocol. An incompatible Aphotic + revision therefore requires a new magic or an explicitly allocated extension + mechanism; it MUST NOT reinterpret an existing repair-mode value. +- PrismShare Stream carries a profile version at frame offset 2. An incompatible + frame or packet-table change MUST allocate a new profile version. + +## 2. Unknown and reserved values + +| location | sender rule | receiver rule | +|---|---|---| +| unknown PrismShare Code format version | MUST NOT emit without a published specification | MUST reject as PrismShare Code | +| unrecognised Aphotic magic | MUST NOT emit as Aphotic | treat as not Aphotic | +| unallocated Aphotic repair mode | none remain in v1 | requires a future incompatible extension; do not guess | +| unknown Stream profile version | MUST NOT emit without a published profile | MUST reject as Stream | +| unknown Stream codec id in a known profile | MUST NOT emit without allocation | MUST ignore the whole frame | +| undefined Stream flag bits | MUST send zero | MUST ignore | +| reserved optical header bits | MUST send zero unless their defining revision says otherwise | follow `FORMAT.md` for the detected version | + +## 3. Compatibility promises + +An editorial revision changes no accepted byte sequence. A normative correction +may narrow or clarify acceptance where the existing prose was contradictory or +unsafe; it must be recorded in `CHANGELOG.md` and accompanied by vectors. A new +wire feature must state: + +1. the allocating version or discriminator; +2. old-reader behaviour; +3. new-reader behaviour for old values; +4. resource bounds; +5. security consequences; and +6. positive and negative conformance vectors. + +## 4. Stability labels + +| label | meaning | +|---|---| +| **proven** | Successfully exercised through a real camera link in the stated operating envelope. | +| **implemented** | Present in the reference implementation and covered by software tests. | +| **defined** | Normatively specified but not proven through a camera. | +| **experimental** | May change or be removed; not a stable conformance target. | +| **falsified** | Implemented and tested, but failed the stated physical objective. | + diff --git a/tools/check_spec.py b/tools/check_spec.py new file mode 100644 index 0000000..7da7d07 --- /dev/null +++ b/tools/check_spec.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +"""Validate PrismShare specification structure and canonical vector integrity.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +STATUS_DOCS = ( + "FORMAT.md", + "DECODER.md", + "BIT-LOADING.md", + "APHOTIC.md", + "STREAM.md", + "BRANDING.md", +) +MIRROR_DOCS = ( + "README.md", + "FORMAT.md", + "DECODER.md", + "BIT-LOADING.md", + "APHOTIC.md", + "STREAM.md", + "BRANDING.md", + "GLOSSARY.md", + "VERSIONING.md", + "CONFORMANCE.md", + "VALIDATION.md", + "SECURITY.md", + "EXAMPLES.md", + "CHANGELOG.md", +) +LINK_RE = re.compile(r"(? None: + raise ValueError(message) + + +def load_json(path: Path) -> dict: + try: + return json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + fail(f"{path.relative_to(ROOT)}: {exc}") + + +def check_status_banners() -> None: + for name in STATUS_DOCS: + text = (ROOT / name).read_text(encoding="utf-8") + if "Document status" not in text[:1600]: + fail(f"{name}: missing top-of-document status banner") + + +def check_local_links() -> None: + for path in ROOT.rglob("*.md"): + text = path.read_text(encoding="utf-8") + for raw in LINK_RE.findall(text): + target = raw.strip().split("#", 1)[0] + if not target or "://" in target or target.startswith("mailto:"): + continue + if not (path.parent / target).resolve().exists(): + fail(f"{path.relative_to(ROOT)}: broken local link {raw!r}") + + +def check_manifest() -> None: + vector_dir = ROOT / "vectors" + manifest = load_json(vector_dir / "manifest.json") + if manifest.get("manifestVersion") != 1: + fail("vectors/manifest.json: unsupported manifestVersion") + listed: set[str] = set() + for entry in manifest.get("files", []): + rel = entry.get("path") + if not isinstance(rel, str) or rel in listed: + fail("vectors/manifest.json: missing or duplicate path") + listed.add(rel) + path = vector_dir / rel + digest = hashlib.sha256(path.read_bytes()).hexdigest() + if digest != entry.get("sha256"): + fail(f"vectors/{rel}: SHA-256 mismatch; got {digest}") + data = load_json(path) + if data.get("status") != entry.get("status"): + fail(f"vectors/{rel}: status disagrees with manifest") + actual = {p.name for p in vector_dir.glob("*.json") if p.name != "manifest.json"} + if listed != actual: + fail(f"vectors/manifest.json: listed={sorted(listed)}, actual={sorted(actual)}") + + +def decode_hex(value: object, label: str) -> bytes: + if not isinstance(value, str) or len(value) % 2 or not re.fullmatch(r"[0-9a-f]*", value): + fail(f"{label}: hex must be lowercase, even-length hexadecimal") + return bytes.fromhex(value) + + +def check_aphotic_vectors() -> None: + data = load_json(ROOT / "vectors" / "aphotic-fountain-v1.json") + source = data["source"] + expected_length = 13 + source["chunkSize"] + for index, value in data["pagePayloadHex"].items(): + page = decode_hex(value, f"Aphotic page {index}") + if len(page) != expected_length or page[:2] != b"PS": + fail(f"Aphotic page {index}: wrong length or magic") + if int.from_bytes(page[2:4], "big") != source["transferId"]: + fail(f"Aphotic page {index}: wrong transfer id") + if int.from_bytes(page[4:6], "big") != int(index): + fail(f"Aphotic page {index}: header index disagrees with vector key") + if int.from_bytes(page[6:8], "big") != source["dataPages"] or page[8] != 0xFF: + fail(f"Aphotic page {index}: wrong dataPages or repair mode") + if int.from_bytes(page[9:13], "big") != source["length"]: + fail(f"Aphotic page {index}: wrong total length") + + +def check_stream_vectors() -> None: + data = load_json(ROOT / "vectors" / "stream-v1.json") + codecs = {0: 13, 8: 7, 9: 4} + for case in data["positive"]: + frame = decode_hex(case["hex"], f"Stream positive {case['id']}") + if len(frame) < 10 or frame[:3] != b"PV\x01": + fail(f"Stream positive {case['id']}: wrong header") + codec = frame[5] >> 4 + count = frame[9] + if codec not in codecs or not 1 <= count <= 200: + fail(f"Stream positive {case['id']}: invalid codec or count") + if len(frame) != 10 + count * codecs[codec]: + fail(f"Stream positive {case['id']}: exact length failure") + packets = [frame[10 + i * codecs[codec] : 10 + (i + 1) * codecs[codec]] for i in range(count)] + if codec == 0 and any(packet[0] != 0x04 for packet in packets): + fail(f"Stream positive {case['id']}: invalid AMR TOC") + if codec in (8, 9) and any(packet[-1] & 0x0F for packet in packets): + fail(f"Stream positive {case['id']}: non-zero Codec2 padding") + for case in data["negative"]: + decode_hex(case["hex"], f"Stream negative {case['id']}") + if case.get("disposition") not in {"reject", "ignore", "not-this-protocol"}: + fail(f"Stream negative {case['id']}: invalid disposition") + + +def check_mirror(mirror: Path) -> None: + for name in MIRROR_DOCS: + source = ROOT / name + target = mirror / name + if not target.exists(): + fail(f"mirror missing {name}") + if source.read_text(encoding="utf-8") != target.read_text(encoding="utf-8"): + fail(f"mirror differs for {name}") + for name in ("README.md", "manifest.json", "aphotic-fountain-v1.json", "stream-v1.json", "symbol-vectors-v1.json"): + source = ROOT / "vectors" / name + target = mirror / "vectors" / name + if not target.exists() or source.read_text(encoding="utf-8") != target.read_text(encoding="utf-8"): + fail(f"mirror differs for vectors/{name}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--mirror", type=Path, help="path to an implementation docs mirror") + args = parser.parse_args() + try: + check_status_banners() + check_local_links() + check_manifest() + check_aphotic_vectors() + check_stream_vectors() + if args.mirror: + check_mirror(args.mirror.resolve()) + except (OSError, KeyError, TypeError, ValueError) as exc: + print(f"spec check failed: {exc}", file=sys.stderr) + return 1 + print("PrismShare specification checks passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/vectors/README.md b/vectors/README.md new file mode 100644 index 0000000..f43ddfa --- /dev/null +++ b/vectors/README.md @@ -0,0 +1,37 @@ +# PrismShare conformance vectors + +Vectors are versioned separately from prose. `manifest.json` lists every vector +file, its SHA-256 digest, status and governing specification. + +## Status meanings + +- `canonical`: required for the applicable conformance claim. +- `draft`: under review and not yet a conformance requirement. +- `scaffold`: reserves the shape of a future vector set but contains no proof. + +The current seed covers deterministic Aphotic Fountain values and PrismShare +Stream frame parsing. Full rendered-symbol vectors remain pending and are +represented by `symbol-vectors-v1.json` as a scaffold rather than being implied +to exist. + +## Rules + +1. Hex strings contain lowercase hexadecimal with no separators. +2. Integers are decimal unless the field name ends in `Hex`. +3. Every negative vector states one expected disposition: `reject`, `ignore`, + or `not-this-protocol`. +4. A canonical file changes only with an explicit changelog entry. +5. Regeneration must be checked by an implementation independent of the + generator before publication. + +## Remaining optical set + +The first full symbol vector release should include: + +- format v2 at one, two and three bits per channel; +- every accepted surround layout and a rejected unknown layout; +- QR versions 2, 7, 20 and 40 at representative correction levels; +- exact plane bytes, matrix modules, rendered pixels and payload hashes; +- mirrored captures and damaged/negative symbols; and +- format v3 profile/header cases clearly labelled defined or experimental. + diff --git a/vectors/aphotic-fountain-v1.json b/vectors/aphotic-fountain-v1.json new file mode 100644 index 0000000..e2aed70 --- /dev/null +++ b/vectors/aphotic-fountain-v1.json @@ -0,0 +1,26 @@ +{ + "vectorSet": "aphotic-fountain-v1", + "status": "canonical", + "source": { + "length": 5000, + "byteAt": "(index * 167) mod 251", + "chunkSize": 64, + "transferId": 19003, + "dataPages": 79, + "repairMode": 255 + }, + "neighborSets": { + "79": [13, 22], + "80": [28, 43, 70, 26], + "81": [62, 8], + "82": [13, 25], + "83": [50, 29], + "84": [3, 36] + }, + "pagePayloadHex": { + "0": "50534a3b0000004fff0000138800a753faa652f9a551f8a450f7a34ff6a24ef5a14df4a04cf39f4bf29e4af19d49f09c48ef9b47ee9a46ed9945ec9844eb9743ea9642e99541e89440e7933fe6", + "78": "50534a3b004e004fff000013885d09b05c08af5b070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "79": "50534a3b004f004fff00001388454dc04b4bc04d45c04f47c04d45c04b5bc0455dc0475fc0455dc03b5bc03d45c03f47c03d45c03b4bc0c54dc0c74fc0c54dc0cb4bc0cd45c0cf47c0cd45c0cb", + "80": "50534a3b0050004fff00001388e0e3b3e8ddb5e8dfb7e8d5ad15d3a30fd5ad0dd7af03adb50da3b377adb575afb77395bd7593a37f95587d9758639d507da35877bd5875bf5873b56075b3684f" + } +} diff --git a/vectors/manifest.json b/vectors/manifest.json new file mode 100644 index 0000000..b638338 --- /dev/null +++ b/vectors/manifest.json @@ -0,0 +1,24 @@ +{ + "manifestVersion": 1, + "suite": "PrismShare Protocol Suite", + "files": [ + { + "path": "aphotic-fountain-v1.json", + "status": "canonical", + "authority": "APHOTIC.md section 5", + "sha256": "a52b558f7966efe4ae2c6391dcadab90e0cba41ee1dfe975a2e9624bbb55b8bf" + }, + { + "path": "stream-v1.json", + "status": "canonical", + "authority": "STREAM.md sections 3 to 5", + "sha256": "ef30afec15a5f9b0486f852da8f99bee733b39005b957304acbf9530b334c33a" + }, + { + "path": "symbol-vectors-v1.json", + "status": "scaffold", + "authority": "FORMAT.md", + "sha256": "1fd039d539970ce6149e4d9bdfdf0d4103c44b84439e7ce670b98ede654d81c0" + } + ] +} diff --git a/vectors/stream-v1.json b/vectors/stream-v1.json new file mode 100644 index 0000000..cb6fd2a --- /dev/null +++ b/vectors/stream-v1.json @@ -0,0 +1,68 @@ +{ + "vectorSet": "prismshare-stream-v1", + "status": "canonical", + "positive": [ + { + "id": "amr-two-packets", + "hex": "505601123400000002020400000000000000000000000004000000000000000000000000", + "session": 4660, + "codecId": 0, + "flags": 0, + "headSequence": 2, + "packetCount": 2, + "packetSequences": [2, 1] + }, + { + "id": "codec2-1300-talkspurt", + "hex": "505601beef8200fffe01123456789abcd0", + "session": 48879, + "codecId": 8, + "flags": 2, + "headSequence": 65534, + "packetCount": 1, + "packetSequences": [65534] + }, + { + "id": "codec2-700c-wrap", + "hex": "5056010001900000000212345670abcdef00", + "session": 1, + "codecId": 9, + "flags": 0, + "headSequence": 0, + "packetCount": 2, + "packetSequences": [0, 16777215] + } + ], + "negative": [ + { + "id": "zero-packets", + "hex": "50560112340000000000", + "disposition": "reject", + "reason": "packet count is below 1" + }, + { + "id": "unknown-profile", + "hex": "50560212340000000000", + "disposition": "reject", + "reason": "profile version 2 is not defined" + }, + { + "id": "unknown-codec", + "hex": "50560112347000000101ff", + "disposition": "ignore", + "reason": "codec id 7 is not allocated in profile version 1" + }, + { + "id": "wrong-length", + "hex": "5056011234000000020104", + "disposition": "reject", + "reason": "AMR packet count requires 13 packet bytes" + }, + { + "id": "amr-wrong-toc", + "hex": "505601123400000001010c000000000000000000000000", + "disposition": "reject", + "reason": "codec id 0 requires the AMR mode-0 TOC" + } + ] +} diff --git a/vectors/symbol-vectors-v1.json b/vectors/symbol-vectors-v1.json new file mode 100644 index 0000000..6549b04 --- /dev/null +++ b/vectors/symbol-vectors-v1.json @@ -0,0 +1,14 @@ +{ + "vectorSet": "prismshare-code-symbol-v1", + "status": "scaffold", + "cases": [], + "requiredCoverage": [ + "format-v2-b1-b2-b3", + "accepted-surround-layouts", + "qr-versions-2-7-20-40", + "plane-bytes-and-modules", + "rendered-pixel-hashes", + "mirrored-and-negative-captures", + "format-v3-profile-headers" + ] +} From 7d4a5f55671d3ec8a3bca41ee8ec8ddd0f9239b4 Mon Sep 17 00:00:00 2001 From: Mixed Guffin <252040198+aXL333@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:49:08 +0930 Subject: [PATCH 3/5] Remove trailing specification whitespace --- CONFORMANCE.md | 1 - EXAMPLES.md | 1 - SECURITY.md | 1 - VERSIONING.md | 1 - vectors/README.md | 1 - 5 files changed, 5 deletions(-) diff --git a/CONFORMANCE.md b/CONFORMANCE.md index 3cf8ae7..5b1360f 100644 --- a/CONFORMANCE.md +++ b/CONFORMANCE.md @@ -78,4 +78,3 @@ Files under `vectors/` are canonical only when their manifest entry has `"status": "canonical"`. A scaffold or draft vector MUST NOT support a conformance claim. Every canonical entry requires an origin description, expected disposition and SHA-256 digest in `vectors/manifest.json`. - diff --git a/EXAMPLES.md b/EXAMPLES.md index 458ebf2..18e5c05 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -86,4 +86,3 @@ matches the empty count. Other negative examples, including an unknown profile and unknown codec, are in `vectors/stream-v1.json` with their required dispositions. - diff --git a/SECURITY.md b/SECURITY.md index bfb19bc..6fdc5a3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -72,4 +72,3 @@ input. Implementations SHOULD use maintained versions, argument arrays rather than shell interpolation, bounded temporary storage and least-privilege process execution. A protocol-conformant frame is not proof that its codec payload is safe for a particular decoder. - diff --git a/VERSIONING.md b/VERSIONING.md index 47d99ac..eeeb8b6 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -52,4 +52,3 @@ wire feature must state: | **defined** | Normatively specified but not proven through a camera. | | **experimental** | May change or be removed; not a stable conformance target. | | **falsified** | Implemented and tested, but failed the stated physical objective. | - diff --git a/vectors/README.md b/vectors/README.md index f43ddfa..e35b972 100644 --- a/vectors/README.md +++ b/vectors/README.md @@ -34,4 +34,3 @@ The first full symbol vector release should include: - exact plane bytes, matrix modules, rendered pixels and payload hashes; - mirrored captures and damaged/negative symbols; and - format v3 profile/header cases clearly labelled defined or experimental. - From 5c6773b896fba8dc527795658827358a0fa52bd8 Mon Sep 17 00:00:00 2001 From: Mixed Guffin <252040198+aXL333@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:50:55 +0930 Subject: [PATCH 4/5] Keep vector checks portable across platforms --- README.md | 6 ------ tools/check_spec.py | 6 +++++- vectors/README.md | 4 +++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3fe51c0..2518141 100644 --- a/README.md +++ b/README.md @@ -107,12 +107,6 @@ PrismShare Code and its Aphotic Transfer and PrismShare Stream payload protocols were created and authored by **[aXL333](https://github.com/aXL333)** and are published by **MASHR Systems**. -## Author - -PrismShare Code and its Aphotic file-transfer and Prism Stream payload protocols -were created and authored by **[aXL333](https://github.com/aXL333)** and are -published by **MASHR Systems**. - ## Acknowledgements The first public revision incorporates a detailed format review by diff --git a/tools/check_spec.py b/tools/check_spec.py index 7da7d07..46b18f4 100644 --- a/tools/check_spec.py +++ b/tools/check_spec.py @@ -80,7 +80,11 @@ def check_manifest() -> None: fail("vectors/manifest.json: missing or duplicate path") listed.add(rel) path = vector_dir / rel - digest = hashlib.sha256(path.read_bytes()).hexdigest() + # Git may materialise text files with CRLF on Windows. Vector hashes are + # defined over their UTF-8 repository form with LF line endings so the + # same immutable JSON has one digest on every checkout platform. + canonical_bytes = path.read_bytes().replace(b"\r\n", b"\n") + digest = hashlib.sha256(canonical_bytes).hexdigest() if digest != entry.get("sha256"): fail(f"vectors/{rel}: SHA-256 mismatch; got {digest}") data = load_json(path) diff --git a/vectors/README.md b/vectors/README.md index e35b972..defdf09 100644 --- a/vectors/README.md +++ b/vectors/README.md @@ -1,7 +1,9 @@ # PrismShare conformance vectors Vectors are versioned separately from prose. `manifest.json` lists every vector -file, its SHA-256 digest, status and governing specification. +file, its SHA-256 digest, status and governing specification. JSON digests are +calculated over UTF-8 bytes with line endings normalized to LF, so Windows and +Unix checkouts verify the same repository content. ## Status meanings From 85ff3bdb300015bdf2b3ac3e2860893ab4afa91b Mon Sep 17 00:00:00 2001 From: Mixed Guffin <252040198+aXL333@users.noreply.github.com> Date: Sat, 15 Aug 2026 15:37:37 +0930 Subject: [PATCH 5/5] Publish PrismShare Code under Blue Heeler Software --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2518141..1732cac 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ property applicable to QR Code encoding and decoding in their jurisdiction. PrismShare Code and its Aphotic Transfer and PrismShare Stream payload protocols were created and authored by **[aXL333](https://github.com/aXL333)** -and are published by **MASHR Systems**. +and are published by **Blue Heeler Software**. ## Acknowledgements