@aicent/rttp 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,6 +22,19 @@ locally — offline, no account, no network. `[PASS]` or it is not.
22
22
  The JavaScript and Python sides **share no code**. They are separate
23
23
  implementations of the same published vectors, and they agree byte for byte.
24
24
 
25
+ ### The other half — Python, on PyPI
26
+
27
+ ```console
28
+ $ pip install rttp
29
+ $ python -m rttp.selftest
30
+ [PASS] all 53 checks passed
31
+ ```
32
+
33
+ Published as **`rttp`** — the bare name, because PyPI has no scopes and the name
34
+ was free. That package ships a **byte-identical copy** of this same vector set and
35
+ replays it offline, exactly as this one does: two registries, two languages, one
36
+ specification.
37
+
25
38
  ---
26
39
 
27
40
  ## Install
@@ -51,6 +64,18 @@ r.canonical_uri // 'rttp://brain.epoekie.aicent/verify'
51
64
  r.route_shard_hex // '459e543b73d86005b72ba77d5756e83c' <- pure computation
52
65
  ```
53
66
 
67
+ The same three lines ship as a runnable file:
68
+
69
+ ```console
70
+ $ node examples/quickstart.mjs
71
+ 459e543b73d86005b72ba77d5756e83c
72
+ ```
73
+
74
+ Its expected output is the shard printed above — the value the published
75
+ conformance vectors pin for that address, so the example can be **checked**
76
+ rather than believed. It is also the value the Python implementation derives,
77
+ from code that shares nothing with this one.
78
+
54
79
  Addressing is **DNS-free** (RFC-002 §10.5): the routing hash is derived from the
55
80
  authority by SHA-256, so no registry, resolver or network is involved. A URI
56
81
  either yields a shard or throws — there is no fallback, and no `rttps`.
@@ -70,24 +95,24 @@ package fails closed everywhere.
70
95
 
71
96
  ---
72
97
 
73
- ## The address and the prefix are different things
98
+ ## The browser's registration prefix — accepted, never advertised
74
99
 
75
- A user agent accepts `navigator.registerProtocolHandler()` only for schemes on its
76
- own safelisted set, or for a scheme beginning `web+`. So a **web page** must use
77
- the `web+rttp:` prefix to register a handler (RFC-002 §10.6) — but that prefix
78
- describes the **client**, never the target.
100
+ A browser lets a page register a protocol handler only for a scheme it already
101
+ knows, or for one carrying the browser's own **reserved registration prefix**. So
102
+ the string a web page hands to a handler is not the bare address.
79
103
 
80
- ```js
81
- parse('web+rttp://logic.zcmk.aicent/pulse').canonical_uri
82
- // 'rttp://logic.zcmk.aicent/pulse' <- always the canonical form, either way
104
+ This package **accepts** that form, because refusing a string a browser is about
105
+ to deliver would break the handler path — but it is a **tolerance, and the
106
+ specification defines no such form**. The canonical `rttp://` address is what
107
+ this package reports, and the only form it ever emits: same authority, same path,
108
+ same **route shard** either way.
83
109
 
84
- parse('web+rttp://logic.zcmk.aicent/pulse').route_shard_hex
85
- === parse('rttp://logic.zcmk.aicent/pulse').route_shard_hex
86
- // true - same authority, same path, same shard
110
+ ```js
111
+ parse('rttp://logic.zcmk.aicent/pulse').canonical_uri
112
+ // 'rttp://logic.zcmk.aicent/pulse' <- always the canonical form
87
113
  ```
88
114
 
89
- The output `scheme` field tells you which form arrived; everything else is
90
- identical.
115
+ The `scheme` field tells you which form arrived; everything else is identical.
91
116
 
92
117
  ---
93
118
 
@@ -111,7 +136,7 @@ field for field, byte for byte.
111
136
 
112
137
  | Field | Meaning |
113
138
  |:---|:---|
114
- | `scheme` | `rttp` or `web+rttp` — which form arrived |
139
+ | `scheme` | Which form arrived: `rttp` for the canonical address |
115
140
  | `intent` | 8 lowercase hex digits (32-bit routing hash) or a readable organ token |
116
141
  | `intent_is_hash` / `intent_hash32` | Whether it is a hash-intent, and its value |
117
142
  | `pillar`, `root` | The other two authority labels |
@@ -162,7 +187,7 @@ what it is for.
162
187
 
163
188
  | Group | Count | What it proves |
164
189
  |:---|:---|---:|
165
- | `positive_uris` | 6 | Accept, canonicalise and derive — including a hash-intent, an omitted action, and the browser-handler prefix |
190
+ | `positive_uris` | 6 | Accept, canonicalise and derive — including a hash-intent and an omitted action. **Five are conformance claims; one is a `tolerance`** — the browser-registration prefix — and the vector set labels it as such |
166
191
  | `negative_uris` | 15 | **Reject** — uppercase, userinfo, query, fragment, port, multi-segment path, wrong authority shape, trailing slash, foreign scheme, surrounding whitespace |
167
192
  | `frame_vectors` | 3 | URI → `ROUTE_SHARD` → the 128-byte `PulseHeader128`, bit for bit |
168
193
  | `compat_vector_spec_rev_0` | 1 | A frame written before the v1.2.6 extension block still verifies |
@@ -172,7 +197,7 @@ Two implementations either agree byte for byte or they do not.
172
197
 
173
198
  ### Why the checker lives in this package, and why that is still honest
174
199
 
175
- `src/conformance.mjs` is written from RFC-002 §10.2 alone and **must never import
200
+ `src/conformance.mjs` is written from RFC-002 §10 alone and **must never import
176
201
  `src/rttp-uri.mjs`**. A conformance suite that reuses the code under test only
177
202
  proves the code equals itself. The two files share no code, so the rule is
178
203
  checkable by reading them.
@@ -215,7 +240,7 @@ agreeing with its own bugs.
215
240
  | Ecosystem | Name | Status |
216
241
  |:---|:---|:---|
217
242
  | crates.io | `rttp` | held by this project |
218
- | PyPI | `rttp` | the Python reference implementation |
243
+ | PyPI | `rttp` | **published** — the Python reference implementation; `pip install rttp` |
219
244
  | npm | **`@aicent/rttp`** | the bare `rttp` name on npm is held by an unrelated, unmaintained 2017 REST helper, and the `rttp` scope cannot be created at all — a personal account already holds that name. This package is therefore published under the organization this project controls; the package name itself is still just `rttp`. |
220
245
 
221
246
  ---
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The whole quickstart, in three lines.
3
+ *
4
+ * node examples/quickstart.mjs
5
+ *
6
+ * Expected output:
7
+ *
8
+ * 459e543b73d86005b72ba77d5756e83c
9
+ *
10
+ * That is not an arbitrary number. It is the ROUTE_SHARD the published
11
+ * conformance vectors pin for this exact address (`positive_uris[1]` and
12
+ * `frame_vectors[1]` in `src/vectors.json`), which is what makes this example
13
+ * checkable rather than merely illustrative - and it is the same value the
14
+ * Python implementation derives, from code that shares nothing with this one.
15
+ */
16
+
17
+ import { parse } from '@aicent/rttp';
18
+
19
+ const r = parse('rttp://brain.epoekie.aicent/verify');
20
+ console.log(r.route_shard_hex); // 459e543b73d86005b72ba77d5756e83c
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicent/rttp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "RTTP for JavaScript: rttp:// intent validation, canonicalisation and ROUTE_SHARD derivation (RFC-002 §10), plus the published conformance vectors and an independent replay of them, including the 128-byte frame. Zero dependencies, no build step.",
5
5
  "keywords": [
6
6
  "rttp",
@@ -37,6 +37,7 @@
37
37
  "files": [
38
38
  "src",
39
39
  "bin",
40
+ "examples",
40
41
  "README.md",
41
42
  "LICENSE"
42
43
  ],
@@ -64,9 +64,11 @@ export function parseUri(uri) {
64
64
  if (uri !== uri.trim()) throw new Error('leading/trailing whitespace');
65
65
 
66
66
  let rest;
67
+ // The browser-registration form is accepted as a **tolerance only**: the
68
+ // specification defines no such form, but a browser hands it to the handler.
67
69
  if (uri.startsWith('web+rttp://')) rest = uri.slice(11);
68
70
  else if (uri.startsWith('rttp://')) rest = uri.slice(7);
69
- else throw new Error('scheme must be rttp:// or web+rttp://');
71
+ else throw new Error('scheme must be rttp:// or the browser-registration form');
70
72
 
71
73
  for (const ch of ['@', '?', '#', '[', ']']) {
72
74
  if (uri.includes(ch)) throw new Error(`illegal char ${ch}`);
@@ -7,7 +7,7 @@
7
7
 
8
8
  /** The canonical field set of a parsed `rttp` URI. */
9
9
  export interface RttpUri {
10
- /** `rttp` for the canonical form, `web+rttp` when the input used the browser-handler prefix. */
10
+ /** The canonical form, or the browser-registration form when that is how the input arrived. */
11
11
  scheme: 'rttp' | 'web+rttp';
12
12
  /** 8 lowercase hex digits (32-bit routing hash) or a readable organ token. */
13
13
  intent: string;
package/src/rttp-uri.mjs CHANGED
@@ -49,12 +49,15 @@ import { createHash } from 'node:crypto';
49
49
  export const SCHEME = 'rttp';
50
50
 
51
51
  /**
52
- * The browser protocol-handler form. A user agent accepts
53
- * `navigator.registerProtocolHandler()` only for schemes on its safelisted set,
54
- * or for a scheme beginning `web+`, so a web page must use this prefix to
55
- * register an `rttp` handler (RFC-002 §10.6). It is a property of the **client**,
56
- * never of the target: both forms are the same address, and the canonical output
57
- * of this module is always `rttp://`.
52
+ * The prefix a user agent requires on a scheme it does not already know, before
53
+ * it will let a page register a protocol handler. It is a property of the
54
+ * **client**, never of the target: both spellings name the same address, and the
55
+ * canonical output of this module is always `rttp://`.
56
+ *
57
+ * Accepted for **tolerance only** — the specification defines no such form. A
58
+ * conforming third-party implementation is not required to reproduce this
59
+ * tolerance; refusing the string a browser is about to deliver would, however,
60
+ * break the handler path.
58
61
  */
59
62
  export const BROWSER_HANDLER_SCHEME = 'web+rttp';
60
63
 
package/src/vectors.json CHANGED
@@ -20,7 +20,9 @@
20
20
  "root": "aicent",
21
21
  "action": "vessel",
22
22
  "action_omitted": false,
23
- "route_shard_hex": "bf77b78ff6ceafc363226aa586562218"
23
+ "route_shard_hex": "bf77b78ff6ceafc363226aa586562218",
24
+ "tolerance": false,
25
+ "note": null
24
26
  },
25
27
  {
26
28
  "uri": "rttp://brain.epoekie.aicent/verify",
@@ -32,7 +34,9 @@
32
34
  "root": "aicent",
33
35
  "action": "verify",
34
36
  "action_omitted": false,
35
- "route_shard_hex": "459e543b73d86005b72ba77d5756e83c"
37
+ "route_shard_hex": "459e543b73d86005b72ba77d5756e83c",
38
+ "tolerance": false,
39
+ "note": null
36
40
  },
37
41
  {
38
42
  "uri": "rttp://f3b2a1c4.rttp.aicent",
@@ -44,7 +48,9 @@
44
48
  "root": "aicent",
45
49
  "action": "",
46
50
  "action_omitted": true,
47
- "route_shard_hex": "bf77b78ff6ceafc363226aa586562218"
51
+ "route_shard_hex": "bf77b78ff6ceafc363226aa586562218",
52
+ "tolerance": false,
53
+ "note": null
48
54
  },
49
55
  {
50
56
  "uri": "rttp://00000000.rpki.aicent/attest",
@@ -56,7 +62,9 @@
56
62
  "root": "aicent",
57
63
  "action": "attest",
58
64
  "action_omitted": false,
59
- "route_shard_hex": "753c105fd0f8bf341811acfaaac85b37"
65
+ "route_shard_hex": "753c105fd0f8bf341811acfaaac85b37",
66
+ "tolerance": false,
67
+ "note": null
60
68
  },
61
69
  {
62
70
  "uri": "web+rttp://logic.zcmk.aicent/pulse",
@@ -68,7 +76,9 @@
68
76
  "root": "aicent",
69
77
  "action": "pulse",
70
78
  "action_omitted": false,
71
- "route_shard_hex": "cdc32d19641c84aca3292de6276de056"
79
+ "route_shard_hex": "cdc32d19641c84aca3292de6276de056",
80
+ "tolerance": true,
81
+ "note": "tolerance - the specification defines no such form; accepted because it is the scheme name under which a handler is registered (RFC-002 §10.6)"
72
82
  },
73
83
  {
74
84
  "uri": "rttp://a-b-c.d-e.f-g/audit-v2",
@@ -80,7 +90,9 @@
80
90
  "root": "f-g",
81
91
  "action": "audit-v2",
82
92
  "action_omitted": false,
83
- "route_shard_hex": "905bf68b86aadf883cda2db9640912de"
93
+ "route_shard_hex": "905bf68b86aadf883cda2db9640912de",
94
+ "tolerance": false,
95
+ "note": null
84
96
  }
85
97
  ],
86
98
  "negative_uris": [
@@ -94,11 +106,11 @@
94
106
  },
95
107
  {
96
108
  "uri": "RTTP://f3b2a1c4.rttp.aicent/vessel",
97
- "reason": "uppercase scheme prefix - canonical form is lowercase (RFC-002 §10.2)"
109
+ "reason": "uppercase scheme prefix - canonical form is lowercase (RFC-002 §10.3)"
98
110
  },
99
111
  {
100
112
  "uri": "WEB+RTTP://f3b2a1c4.rttp.aicent/vessel",
101
- "reason": "uppercase scheme prefix on the handler form (RFC-002 §10.2)"
113
+ "reason": "uppercase scheme prefix on the handler form (RFC-002 §10.3)"
102
114
  },
103
115
  {
104
116
  "uri": "rttp://subject@rttp.aicent/vessel",