rttp 0.1.2__tar.gz → 1.2.7__tar.gz
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.
- rttp-1.2.7/CHANGELOG.md +228 -0
- {rttp-0.1.2 → rttp-1.2.7}/PKG-INFO +55 -45
- {rttp-0.1.2 → rttp-1.2.7}/README.md +52 -42
- {rttp-0.1.2 → rttp-1.2.7}/pyproject.toml +4 -4
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/__init__.py +8 -8
- rttp-1.2.7/src/rttp/aid.py +31 -0
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/pulse_header.py +49 -60
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/rttp_uri.py +56 -63
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/seal.py +16 -16
- rttp-1.2.7/src/rttp/seal_asym.py +486 -0
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/selftest.py +248 -53
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp/vectors/rttp-conformance-v1.2.6.json +103 -13
- rttp-0.1.2/CHANGELOG.md +0 -178
- rttp-0.1.2/src/rttp/aid.py +0 -35
- rttp-0.1.2/src/rttp/seal_asym.py +0 -371
- {rttp-0.1.2 → rttp-1.2.7}/LICENSE +0 -0
- {rttp-0.1.2 → rttp-1.2.7}/MANIFEST.in +0 -0
- {rttp-0.1.2 → rttp-1.2.7}/setup.cfg +0 -0
- {rttp-0.1.2 → rttp-1.2.7}/src/rttp.egg-info/SOURCES.txt +0 -0
rttp-1.2.7/CHANGELOG.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Changelog -- `rttp` (Python)
|
|
2
|
+
|
|
3
|
+
All notable changes to the `rttp` package. Format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/); versioning is
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [1.2.7] -- 2026-09-21
|
|
10
|
+
|
|
11
|
+
**Conformance surface grown; wire format untouched.** The shipped
|
|
12
|
+
`rttp/vectors/rttp-conformance-v1.2.6.json` is now 13,460 bytes
|
|
13
|
+
(`sha256 4b743e63...`) and the self-test replays every added case: **83 checks**
|
|
14
|
+
in full, and **80 passed with 3 skipped** on a zero-dependency install -- a check
|
|
15
|
+
that cannot be performed is reported as skipped, never as passed.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
* **Nine fail-closed frame cases, one positive frame.** Each of the nine frames
|
|
20
|
+
must be rejected; the positive frame must be accepted with an empty action.
|
|
21
|
+
* **The published Ed25519 envelope vector**, replayed three ways: the canonical
|
|
22
|
+
signing input byte for byte, `AID == SHA-256(public key)`, and the signature
|
|
23
|
+
itself (the third one skips without the optional primitive).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
* **Skipped checks are now counted and printed**, so a default install can no
|
|
28
|
+
longer look like a full pass.
|
|
29
|
+
* Per-section counts are reported: 38 conformance, 16 envelope rules, 22 envelope
|
|
30
|
+
flow, 2 RFC 8032, 5 zero-dependency.
|
|
31
|
+
|
|
32
|
+
### Note
|
|
33
|
+
|
|
34
|
+
* PyPI and npm carry `1.2.7`; crates.io carries the same code as the pre-release
|
|
35
|
+
`1.2.8-alpha`. The registry version strings differ by publishing policy, not by
|
|
36
|
+
code -- the vectors and their `sha256` are identical everywhere.
|
|
37
|
+
|
|
38
|
+
## [1.2.6] -- 2026-09-19
|
|
39
|
+
|
|
40
|
+
**Version alignment -- no code change.** Every module, byte layout and
|
|
41
|
+
conformance vector is identical to 0.1.2, and the self-test still reports the
|
|
42
|
+
the same 53 checks (full set, i.e. with the optional Ed25519 backend). Only the version string and the maturity classifier change.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
* **The version number now mirrors the stack version.** PyPI and npm carry
|
|
47
|
+
`1.2.6`, so a single number identifies this code on every distribution
|
|
48
|
+
channel; crates.io carries the same code as the pre-release `1.2.6-alpha`.
|
|
49
|
+
The old `0.1.x` line was a package-maturity number that corresponded to
|
|
50
|
+
nothing in the specification, and it made the same package look like two
|
|
51
|
+
different releases on two registries.
|
|
52
|
+
* **`Development Status` classifier: `3 - Alpha` -> `4 - Beta`.** A
|
|
53
|
+
non-pre-release version must not ship with an "Alpha" classifier; leaving
|
|
54
|
+
the two in conflict invites the question "which one is true?".
|
|
55
|
+
* Install command note: on crates.io every published version of this crate is
|
|
56
|
+
a pre-release, so Cargo will not select it from a plain `rttp` requirement.
|
|
57
|
+
Write `cargo add rttp@1.2.6-alpha` (or pin `rttp = "1.2.6-alpha"`).
|
|
58
|
+
|
|
59
|
+
## [0.1.2] -- 2026-09-18
|
|
60
|
+
|
|
61
|
+
**Metadata correction -- no code change.** Every module, byte layout and
|
|
62
|
+
conformance vector is identical to 0.1.1, and the self-test still reports the
|
|
63
|
+
the same 53 checks (full set, i.e. with the optional Ed25519 backend).
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
* **The distribution identity now matches the IANA registration.** The package
|
|
68
|
+
was authored as `RTTP Working Group` -- a name that appears in no filing. The
|
|
69
|
+
`rttp` URI-scheme registration on file with IANA (ticket `[IANA #1459939]`)
|
|
70
|
+
names **`RTTP.COM Organization`** as the `Author/Change controller`, and every
|
|
71
|
+
other public surface uses that same name. Both package manifests now say it
|
|
72
|
+
too:
|
|
73
|
+
|
|
74
|
+
| Field | Before | After |
|
|
75
|
+
|:---|:---|:---|
|
|
76
|
+
| `pyproject.toml` -> `authors` (PyPI) | `RTTP Working Group` | **`RTTP.COM Organization`** |
|
|
77
|
+
| `package.json` -> `author` (npm) | `RTTP Working Group` | **`RTTP.COM Organization`** |
|
|
78
|
+
|
|
79
|
+
Package metadata cannot be edited after publication, so 0.1.0 and 0.1.1 keep
|
|
80
|
+
the old string inside their own metadata; this is the version that carries the
|
|
81
|
+
correction. Project pages display the latest version, so the visible author
|
|
82
|
+
name becomes correct once this release is out.
|
|
83
|
+
|
|
84
|
+
## [0.1.1] -- 2026-09-18
|
|
85
|
+
|
|
86
|
+
**Documentation only -- no code change.** Every module, byte layout and
|
|
87
|
+
conformance vector is identical to 0.1.0, and the self-test still reports the
|
|
88
|
+
the same 53 checks (full set, i.e. with the optional Ed25519 backend).
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
* **A distribution claim that was wrong, not merely outdated.** The 0.1.0 entry
|
|
93
|
+
below said the JavaScript side "will publish under the `@rttp` scope". That
|
|
94
|
+
scope **cannot be created at all**: npm already has a personal account named
|
|
95
|
+
`rttp`, and an organization name may not collide with a user name. The
|
|
96
|
+
JavaScript side is published as **`@aicent/rttp`** -- this project's own
|
|
97
|
+
organization, with the package name itself still just `rttp`.
|
|
98
|
+
* **README distribution table** now names the npm package exactly
|
|
99
|
+
(`@aicent/rttp`), and gives `pip install rttp` for this package, matching the
|
|
100
|
+
table in the JavaScript package's README.
|
|
101
|
+
|
|
102
|
+
### Distribution status at this release
|
|
103
|
+
|
|
104
|
+
| Registry | Name | State |
|
|
105
|
+
|:---|:---|:---|
|
|
106
|
+
| PyPI | `rttp` | this package -- `pip install rttp` |
|
|
107
|
+
| npm | `@aicent/rttp` | **published 0.1.1** -- 2026-09-18 |
|
|
108
|
+
| crates.io | `rttp` | held by this project |
|
|
109
|
+
|
|
110
|
+
## [0.1.0] -- 2026-09-17
|
|
111
|
+
|
|
112
|
+
First release. Scope is deliberately narrow: **framing, addressing and sealing**,
|
|
113
|
+
with zero dependencies and no transport.
|
|
114
|
+
|
|
115
|
+
### Added
|
|
116
|
+
|
|
117
|
+
* **`rttp.pulse_header`** -- `PulseHeader128` codec for the 128-byte
|
|
118
|
+
hardware-aligned frame header (RFC-002 sec. 4.1), including the v1.2.6 extension
|
|
119
|
+
block:
|
|
120
|
+
* `SPEC_REV` `0x66` - `FLAGS` `0x67` - `ACTION_LEN` `0x68` - `ACTION` `0x69`(16B)
|
|
121
|
+
- `RESERVED` `0x79`(7B)
|
|
122
|
+
* bytes `0x00`-`0x65` are **not touched**, `VERSION_ID` stays 130 -- readers
|
|
123
|
+
that only understand the original layout remain conformant
|
|
124
|
+
* `build()` - `build_for_uri()` - `parse()` - `verify()` - `to_hex()` /
|
|
125
|
+
`from_hex()`
|
|
126
|
+
* strict verification (R1-R7): unknown `SPEC_REV` rejected, `SPEC_REV=0` with a
|
|
127
|
+
non-zero block rejected, non-zero `RESERVED` rejected, unknown `FLAGS` bits
|
|
128
|
+
rejected, `ACTION` padding must be zero, `ACTION_LEN` must agree with the
|
|
129
|
+
payload
|
|
130
|
+
* **`rttp.rttp_uri`** -- validation, canonicalisation and `ROUTE_SHARD`
|
|
131
|
+
derivation for `rttp://` URIs (RFC-002 sec. 10.2 / sec. 10.3):
|
|
132
|
+
* `ROUTE_SHARD = SHA-256(ASCII(canonical_authority))[0:16]` -- deterministic,
|
|
133
|
+
pure computation, **no DNS** (sec. 10.5)
|
|
134
|
+
* hash-intent (`8lowhex`) and name-intent forms; open-set `action` vocabulary
|
|
135
|
+
* fail-closed rejection of uppercase, `userinfo`, `port`, `query`,
|
|
136
|
+
`fragment`, multi-segment paths, wrong segment counts, leading/trailing `-`,
|
|
137
|
+
whitespace, and non-`rttp` schemes
|
|
138
|
+
* accepts both `rttp://` and `web+rttp://` -- the second as a **tolerance** for
|
|
139
|
+
the browser protocol-handler form, which RFC-002 no longer defines
|
|
140
|
+
(2026-09-17). Not a conformance claim; see "Known gaps" below.
|
|
141
|
+
* **`rttp.seal`** -- managed profile: HMAC-SHA256 over a pre-shared key table,
|
|
142
|
+
bidirectional switch<->agent signing, `ts|nonce` with a 120-second skew window.
|
|
143
|
+
* **`rttp.seal_asym`** -- **sovereign profile**: Ed25519 seals with
|
|
144
|
+
`AID = SHA-256(public_key)`.
|
|
145
|
+
* self-certifying: the envelope carries the public key, so a verifier
|
|
146
|
+
recomputes the identity instead of trusting a claim -- **no issuance, no
|
|
147
|
+
registry, no directory**
|
|
148
|
+
* `ts` and `nonce` are **inside** the signed input, so they cannot be rewritten
|
|
149
|
+
without breaking the signature
|
|
150
|
+
* freshness enforced by default (`|now - ts| <= 120 s`); archival verification
|
|
151
|
+
available explicitly via `check_freshness=False`
|
|
152
|
+
* lowercase-hex only -- a case variant is rejected, not normalised
|
|
153
|
+
* `generate_keypair()` - `seal()` - `unseal()` - `verify_envelope()` -
|
|
154
|
+
`verify_claims()` - `public_bundle()` - `signing_input()`
|
|
155
|
+
* requires the `ed25519` extra; without it every call raises `SealError` with
|
|
156
|
+
the install instruction rather than degrading to an unsigned path
|
|
157
|
+
* **`rttp.aid`** -- Autonomous Identity derivation (RFC-001), shared by both
|
|
158
|
+
profiles so an AID means one thing across the stack.
|
|
159
|
+
* **`rttp.selftest`** -- `python -m rttp.selftest`, **53 checks with the optional Ed25519 backend (29 by default)**:
|
|
160
|
+
* 25 conformance vectors (6 positive URIs - 15 rejections - 3 deterministic
|
|
161
|
+
frames - 1 `SPEC_REV=0` compatibility vector)
|
|
162
|
+
* RFC 8032 sec. 7.1 TEST 1 / TEST 2, proving the signature backend is standard
|
|
163
|
+
Ed25519 -- so a failure identifies *which* half broke
|
|
164
|
+
* 22 envelope checks, including a 14-case tamper matrix in which every single
|
|
165
|
+
alteration must be rejected, and an explicit replay-window test
|
|
166
|
+
* 4 static-scan checks proving the core modules import nothing outside the
|
|
167
|
+
standard library
|
|
168
|
+
* `--vectors` prints the deterministic envelope vector for the specification;
|
|
169
|
+
`--quiet` prints the summary line only
|
|
170
|
+
* **`rttp/vectors/rttp-conformance-v1.2.6.json`** -- the published vectors, shipped
|
|
171
|
+
as package data so the self-test runs from an installed wheel, offline.
|
|
172
|
+
* Console entry point `rttp-selftest`.
|
|
173
|
+
* **`MANIFEST.in`** -- the source distribution carries `README.md`,
|
|
174
|
+
`CHANGELOG.md`, `LICENSE`, `pyproject.toml` and the published vectors, and
|
|
175
|
+
excludes virtual environments and build noise. Both the wheel and the sdist
|
|
176
|
+
pass `twine check`.
|
|
177
|
+
|
|
178
|
+
### Notes
|
|
179
|
+
|
|
180
|
+
* **Zero dependencies** in the core, and that is a machine-checked claim rather
|
|
181
|
+
than a promise -- see the static-scan section of the self-test.
|
|
182
|
+
* The reference implementation evaluates the
|
|
183
|
+
**`iqa`** namespace nowhere: this package is the `rttp` protocol only.
|
|
184
|
+
* ~~Distribution: PyPI `rttp`. The npm name `rttp` is held by an unrelated,
|
|
185
|
+
unmaintained project, so the JavaScript side will publish under the `@rttp`
|
|
186
|
+
scope.~~ -- **corrected 2026-09-18.** PyPI `rttp` is now **published** (0.1.0,
|
|
187
|
+
2026-09-17). The npm name `rttp` is indeed held by an unrelated, unmaintained
|
|
188
|
+
project -- but the **`@rttp` scope cannot be created at all**, because a
|
|
189
|
+
personal account already holds that name. The JavaScript side is therefore
|
|
190
|
+
published as **`@aicent/rttp`**: this project's own organization, with the
|
|
191
|
+
package name still just `rttp`.
|
|
192
|
+
|
|
193
|
+
### Known gaps
|
|
194
|
+
|
|
195
|
+
Tracked in `SPEC/RTTP-SEAL-ENVELOPE-v1.2.6.md` sec. 8 and sec. 10. Deliberately listed
|
|
196
|
+
here so nobody has to discover them:
|
|
197
|
+
|
|
198
|
+
* No transport and no client. The core encodes, addresses and seals; sending is a
|
|
199
|
+
separate package's job.
|
|
200
|
+
* No revocation, and no key rotation story. Both belong to RFC-003.
|
|
201
|
+
* No replay **cache**: freshness (sec. 3.4) bounds the window, but within it a replayed
|
|
202
|
+
envelope still verifies unless the verifier keeps a short-lived nonce store.
|
|
203
|
+
* No transport-origin binding: the seal proves who signed, not which connection
|
|
204
|
+
delivered it.
|
|
205
|
+
* Rust parity: the published `rttp` crate has no transport layer and no envelope.
|
|
206
|
+
Python and Node are the conforming implementations so far.
|
|
207
|
+
* ~~the browser-registration prefix is accepted because RFC-002 sec. 10.6 requires
|
|
208
|
+
it...~~ -- **superseded 2026-09-17.** That form was removed from both
|
|
209
|
+
specifications and will never be submitted to IANA. `rttp_uri` still accepts it
|
|
210
|
+
as a **tolerance** -- a browser hands the handler that string, and refusing it
|
|
211
|
+
would break the protocol-handler path -- but the specification defines no such
|
|
212
|
+
form, so the acceptance is **outside the specification** and a conforming
|
|
213
|
+
third-party implementation is not required to reproduce it.
|
|
214
|
+
* ~~**Open, and ours to fix:** the published vectors asserted things the
|
|
215
|
+
specification no longer said.~~ -- **resolved 2026-09-18.** The generator now
|
|
216
|
+
classifies every forward case explicitly:
|
|
217
|
+
* `positive_uris` -- each entry carries `tolerance` (boolean) and `note`. The
|
|
218
|
+
case whose `uri` carries the browser-registration prefix is `tolerance: true`,
|
|
219
|
+
with its reason recorded: it is accepted only because that is the scheme name
|
|
220
|
+
under which a handler is registered (RFC-002 sec. 10.6) -- never because the
|
|
221
|
+
specification defines it. It is **not** a conformance claim, and the 25-check
|
|
222
|
+
total is unchanged.
|
|
223
|
+
* `negative_uris` -- the two case-clause citations were corrected from
|
|
224
|
+
`(RFC-002 sec. 10.2)` to **`sec. 10.3`**, the clause that actually carries the
|
|
225
|
+
lowercase rule. sec. 10.2 now appears nowhere in the vector set.
|
|
226
|
+
Regenerated (`SPEC/tools/conformance.py --generate`), replayed (`--check`,
|
|
227
|
+
25/25), and mirrored byte-identically into both packages. After the change:
|
|
228
|
+
Python self-test 53/53, Node runner 25/25.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rttp
|
|
3
|
-
Version:
|
|
4
|
-
Summary: RTTP reference implementation: PulseHeader128 framing, rttp:// addressing, and self-certifying Ed25519 seals (RFC-002
|
|
3
|
+
Version: 1.2.7
|
|
4
|
+
Summary: RTTP reference implementation: PulseHeader128 framing, rttp:// addressing, and self-certifying Ed25519 seals (RFC-002 sec. 4.1 / sec. 10 / sec. 11).
|
|
5
5
|
Author: RTTP.COM Organization
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://rttp.com/
|
|
8
8
|
Project-URL: Specification, https://rttp.com/RFC-002/
|
|
9
9
|
Keywords: rttp,protocol,ai-agents,intent-addressing,uri-scheme,ed25519,conformance
|
|
10
|
-
Classifier: Development Status ::
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -31,35 +31,45 @@ Dynamic: license-file
|
|
|
31
31
|
128-byte `PulseHeader128` frame, `rttp://` intent addressing, and self-certifying
|
|
32
32
|
Ed25519 seals.**
|
|
33
33
|
|
|
34
|
-
[RFC-002
|
|
34
|
+
[RFC-002 sec. 4.1](https://rttp.com/RFC-002/) - [sec. 10](https://rttp.com/RFC-002/) - sec. 11 - v1.2.6
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## Verify it in one command
|
|
38
|
+
## Verify it in one command -- no trust required
|
|
39
39
|
|
|
40
40
|
```console
|
|
41
41
|
$ pip install rttp
|
|
42
42
|
$ python -m rttp.selftest
|
|
43
|
-
rttp
|
|
43
|
+
rttp 1.2.7 self-test
|
|
44
44
|
python 3.14.2 on win32
|
|
45
45
|
...
|
|
46
|
-
[PASS] all
|
|
46
|
+
[PASS] all 80 checks passed (3 skipped) # default install: the Ed25519 checks are skipped
|
|
47
|
+
|
|
48
|
+
$ pip install rttp[ed25519]
|
|
49
|
+
$ python -m rttp.selftest
|
|
50
|
+
rttp 1.2.7 self-test
|
|
51
|
+
python 3.14.2 on win32
|
|
52
|
+
...
|
|
53
|
+
[PASS] all 83 checks passed # with the optional Ed25519 backend
|
|
47
54
|
```
|
|
48
55
|
|
|
49
56
|
That is the point of this package. A specification is worth exactly what an
|
|
50
|
-
independent implementation can reproduce from it
|
|
51
|
-
believe a table of numbers, this ships the vectors and replays them locally
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* **
|
|
57
|
+
independent implementation can reproduce from it -- so instead of asking you to
|
|
58
|
+
believe a table of numbers, this ships the vectors and replays them locally.
|
|
59
|
+
The 83 checks below are the full set, i.e. with the optional Ed25519 backend
|
|
60
|
+
installed; a default install runs 80 checks and skips 3.
|
|
61
|
+
|
|
62
|
+
* **38 conformance checks** -- `rttp://` parsing, fail-closed rejections, and the
|
|
63
|
+
URI -> `ROUTE_SHARD` -> 128-byte frame chain, bit for bit.
|
|
64
|
+
* **2 RFC 8032 checks** -- the Ed25519 backend is shown to be *standard* Ed25519,
|
|
56
65
|
not a lookalike, so a failure tells you which half broke.
|
|
57
|
-
* **
|
|
58
|
-
14-case tamper matrix where
|
|
59
|
-
|
|
66
|
+
* **38 envelope checks** -- 16 rule-layer checks that need no primitive, plus
|
|
67
|
+
round-trip, self-certification, freshness and a 14-case tamper matrix where
|
|
68
|
+
every alteration must be rejected.
|
|
69
|
+
* **5 zero-dependency checks** -- a static scan proving the core imports nothing
|
|
60
70
|
outside the standard library.
|
|
61
71
|
|
|
62
|
-
Offline. No account. No network. `[PASS]` or it is not.
|
|
72
|
+
Offline. No account. No network. `[PASS]` or it is not. Every vector file shipped here is listed in `vectors.manifest.json` with its byte count and SHA-256, produced by a generator rather than typed by hand - recompute the hash yourself if you prefer.
|
|
63
73
|
|
|
64
74
|
---
|
|
65
75
|
|
|
@@ -73,7 +83,7 @@ pip install rttp[ed25519] # + sovereign-profile seals (Ed25519)
|
|
|
73
83
|
Python 3.9+. No compiled extensions in the core.
|
|
74
84
|
|
|
75
85
|
The core deliberately has **no dependencies**. A protocol reference that cannot be
|
|
76
|
-
read without resolving a dependency tree is not much of a reference
|
|
86
|
+
read without resolving a dependency tree is not much of a reference -- and an
|
|
77
87
|
air-gapped reviewer should be able to check the frames.
|
|
78
88
|
|
|
79
89
|
---
|
|
@@ -82,13 +92,13 @@ air-gapped reviewer should be able to check the frames.
|
|
|
82
92
|
|
|
83
93
|
| Module | What it does | Authority |
|
|
84
94
|
|:---|:---|:---|
|
|
85
|
-
| `rttp.pulse_header` | `PulseHeader128` codec
|
|
86
|
-
| `rttp.rttp_uri` | Validate, canonicalise and derive `ROUTE_SHARD` from an `rttp` URI | RFC-002
|
|
87
|
-
| `rttp.seal` | **Managed profile**
|
|
88
|
-
| `rttp.seal_asym` | **Sovereign profile**
|
|
95
|
+
| `rttp.pulse_header` | `PulseHeader128` codec -- build / parse / verify the 128-byte hardware-aligned header, including the v1.2.6 extension block at `0x66`-`0x7F` | RFC-002 sec. 4.1 + `SPEC/RTTP-FRAME-EXT-v1.2.6.md` |
|
|
96
|
+
| `rttp.rttp_uri` | Validate, canonicalise and derive `ROUTE_SHARD` from an `rttp` URI | RFC-002 sec. 10.2 / sec. 10.3 ABNF |
|
|
97
|
+
| `rttp.seal` | **Managed profile** -- symmetric HMAC-SHA256 over a pre-shared key table | -- |
|
|
98
|
+
| `rttp.seal_asym` | **Sovereign profile** -- Ed25519, self-certifying, no issuance and no registry | `SPEC/RTTP-SEAL-ENVELOPE-v1.2.6.md` (draft) |
|
|
89
99
|
| `rttp.aid` | Autonomous Identity derivation | RFC-001 |
|
|
90
100
|
|
|
91
|
-
Plus `rttp.vectors`
|
|
101
|
+
Plus `rttp.vectors` -- the published conformance vectors, shipped inside the wheel
|
|
92
102
|
so the self-test works from an installed package with no repository checkout.
|
|
93
103
|
|
|
94
104
|
---
|
|
@@ -106,9 +116,9 @@ parsed["action"] # 'verify'
|
|
|
106
116
|
parsed["route_shard"].hex() # 16-byte routing hash, pure computation
|
|
107
117
|
```
|
|
108
118
|
|
|
109
|
-
Addressing is **DNS-free** (RFC-002
|
|
119
|
+
Addressing is **DNS-free** (RFC-002 sec. 10.5): the routing hash is derived from the
|
|
110
120
|
authority by SHA-256, so no registry, resolver or network is involved. Malformed
|
|
111
|
-
input is rejected rather than normalised
|
|
121
|
+
input is rejected rather than normalised -- a case variant is not a spelling
|
|
112
122
|
difference, it is a different string.
|
|
113
123
|
|
|
114
124
|
### Build a frame from a URI
|
|
@@ -126,9 +136,9 @@ len(raw) # 128
|
|
|
126
136
|
pulse_header.verify(raw)["action"] # 'verify'
|
|
127
137
|
```
|
|
128
138
|
|
|
129
|
-
`build_for_uri` performs the mapping RFC-002
|
|
139
|
+
`build_for_uri` performs the mapping RFC-002 sec. 10.4 promises: the URI's authority
|
|
130
140
|
becomes `ROUTE_SHARD`, its path becomes `ACTION`. Readers that only understand
|
|
131
|
-
`0x00
|
|
141
|
+
`0x00`-`0x65` keep working -- the extension block occupies bytes that were already
|
|
132
142
|
zero, and `VERSION_ID` stays 130.
|
|
133
143
|
|
|
134
144
|
### Seal with your own identity
|
|
@@ -152,7 +162,7 @@ credentials to obtain, and no operator who could refuse to issue them.
|
|
|
152
162
|
|
|
153
163
|
## Two profiles, and when each applies
|
|
154
164
|
|
|
155
|
-
| | **Managed**
|
|
165
|
+
| | **Managed** -- `seal` | **Sovereign** -- `seal_asym` |
|
|
156
166
|
|:---|:---|:---|
|
|
157
167
|
| Primitive | HMAC-SHA256 | Ed25519 |
|
|
158
168
|
| Key material | pre-shared table | self-generated |
|
|
@@ -161,7 +171,7 @@ credentials to obtain, and no operator who could refuse to issue them.
|
|
|
161
171
|
| A stranger can participate | no | **yes** |
|
|
162
172
|
| One leaked key | can forge any peer | forges one identity |
|
|
163
173
|
|
|
164
|
-
Inside a set of symmetric secrets there is no such thing as a stranger
|
|
174
|
+
Inside a set of symmetric secrets there is no such thing as a stranger -- whoever
|
|
165
175
|
holds the shared key can mint anybody's seal. The managed profile is therefore for
|
|
166
176
|
a closed organism, where a fixed set of roles genuinely do trust each other.
|
|
167
177
|
|
|
@@ -170,19 +180,19 @@ preference; it is the difference between a private system and a public one.
|
|
|
170
180
|
|
|
171
181
|
---
|
|
172
182
|
|
|
173
|
-
## Scope
|
|
183
|
+
## Scope -- what this package is not
|
|
174
184
|
|
|
175
185
|
| | |
|
|
176
186
|
|:---|:---|
|
|
177
|
-
|
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
187
|
+
| OK **Framing** | Real, and specified. |
|
|
188
|
+
| OK **Addressing** | Real, and specified. |
|
|
189
|
+
| OK **Sealing** | Real, and specified (draft). |
|
|
190
|
+
| NO **Transport** | **Not here.** This package encodes, addresses and seals. It does not open sockets, and there is no `send()` -- a client and its transport belong to a separate package, so that the codec stays dependency-free and auditable. |
|
|
191
|
+
| NO **Routing service** | Not here. `ROUTE_SHARD` is computed locally; delivering a frame to that hash is an operator's job, not the codec's. |
|
|
192
|
+
| NO **Confidentiality** | Signing is not encryption. |
|
|
183
193
|
|
|
184
194
|
**The seal is an envelope around the frame, not bytes inside it.** A reader that
|
|
185
|
-
parses only the 128-byte header sees no seal at all
|
|
195
|
+
parses only the 128-byte header sees no seal at all -- which is why the two
|
|
186
196
|
concerns are specified separately.
|
|
187
197
|
|
|
188
198
|
Unknown revisions, algorithms and malformed input **fail closed** everywhere.
|
|
@@ -193,7 +203,7 @@ Nothing in this package is ever accepted because a check could not be performed.
|
|
|
193
203
|
## Conformance vectors
|
|
194
204
|
|
|
195
205
|
`rttp/vectors/rttp-conformance-v1.2.6.json` is generated, not hand-edited. Each
|
|
196
|
-
vector is deterministic: fixed sequence numbers, fixed timestamp, fixed AID
|
|
206
|
+
vector is deterministic: fixed sequence numbers, fixed timestamp, fixed AID -- so
|
|
197
207
|
two implementations either agree byte for byte or they do not.
|
|
198
208
|
|
|
199
209
|
The vector set is the intended deliverable for third-party certification: pass it
|
|
@@ -207,21 +217,21 @@ needing to trust the authors.
|
|
|
207
217
|
| Ecosystem | Name | Status |
|
|
208
218
|
|:---|:---|:---|
|
|
209
219
|
| crates.io | `rttp` | held by this project |
|
|
210
|
-
| PyPI | `rttp` | **this package**
|
|
211
|
-
| npm | **`@aicent/rttp`** | the unscoped `rttp` name on npm is held by an unrelated, unmaintained 2017 REST helper, and the `rttp` scope cannot be created at all
|
|
220
|
+
| PyPI | `rttp` | **this package** -- `pip install rttp` |
|
|
221
|
+
| npm | **`@aicent/rttp`** | the unscoped `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 -- so the JavaScript side is published under this project's own organization. The package name itself is still just `rttp` |
|
|
212
222
|
|
|
213
223
|
---
|
|
214
224
|
|
|
215
225
|
## Specification status
|
|
216
226
|
|
|
217
|
-
* **`rttp` URI scheme**
|
|
227
|
+
* **`rttp` URI scheme** -- submitted to IANA under RFC 7595, ticket **#1459939**,
|
|
218
228
|
Provisional (First Come, First Served), **under review**. It is **not yet
|
|
219
|
-
registered**
|
|
229
|
+
registered** -- as of 2026-09-15 the IANA URI Schemes registry contains no
|
|
220
230
|
`rttp` entry. Please describe it that way.
|
|
221
|
-
* **Frame layout**
|
|
222
|
-
* **Seal envelope**
|
|
231
|
+
* **Frame layout** -- RFC-002 sec. 4.1, extended by `SPEC/RTTP-FRAME-EXT-v1.2.6.md`.
|
|
232
|
+
* **Seal envelope** -- `SPEC/RTTP-SEAL-ENVELOPE-v1.2.6.md`, a **draft**: the format
|
|
223
233
|
is implemented and vector-tested, but it is not yet a numbered RFC-002 section.
|
|
224
|
-
* **Managed profile**
|
|
234
|
+
* **Managed profile** -- implementation only; it predates the sovereign profile and
|
|
225
235
|
has no standalone specification.
|
|
226
236
|
|
|
227
237
|
Where this package and a specification disagree, **the specification wins and the
|
|
@@ -4,35 +4,45 @@
|
|
|
4
4
|
128-byte `PulseHeader128` frame, `rttp://` intent addressing, and self-certifying
|
|
5
5
|
Ed25519 seals.**
|
|
6
6
|
|
|
7
|
-
[RFC-002
|
|
7
|
+
[RFC-002 sec. 4.1](https://rttp.com/RFC-002/) - [sec. 10](https://rttp.com/RFC-002/) - sec. 11 - v1.2.6
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Verify it in one command
|
|
11
|
+
## Verify it in one command -- no trust required
|
|
12
12
|
|
|
13
13
|
```console
|
|
14
14
|
$ pip install rttp
|
|
15
15
|
$ python -m rttp.selftest
|
|
16
|
-
rttp
|
|
16
|
+
rttp 1.2.7 self-test
|
|
17
17
|
python 3.14.2 on win32
|
|
18
18
|
...
|
|
19
|
-
[PASS] all
|
|
19
|
+
[PASS] all 80 checks passed (3 skipped) # default install: the Ed25519 checks are skipped
|
|
20
|
+
|
|
21
|
+
$ pip install rttp[ed25519]
|
|
22
|
+
$ python -m rttp.selftest
|
|
23
|
+
rttp 1.2.7 self-test
|
|
24
|
+
python 3.14.2 on win32
|
|
25
|
+
...
|
|
26
|
+
[PASS] all 83 checks passed # with the optional Ed25519 backend
|
|
20
27
|
```
|
|
21
28
|
|
|
22
29
|
That is the point of this package. A specification is worth exactly what an
|
|
23
|
-
independent implementation can reproduce from it
|
|
24
|
-
believe a table of numbers, this ships the vectors and replays them locally
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* **
|
|
30
|
+
independent implementation can reproduce from it -- so instead of asking you to
|
|
31
|
+
believe a table of numbers, this ships the vectors and replays them locally.
|
|
32
|
+
The 83 checks below are the full set, i.e. with the optional Ed25519 backend
|
|
33
|
+
installed; a default install runs 80 checks and skips 3.
|
|
34
|
+
|
|
35
|
+
* **38 conformance checks** -- `rttp://` parsing, fail-closed rejections, and the
|
|
36
|
+
URI -> `ROUTE_SHARD` -> 128-byte frame chain, bit for bit.
|
|
37
|
+
* **2 RFC 8032 checks** -- the Ed25519 backend is shown to be *standard* Ed25519,
|
|
29
38
|
not a lookalike, so a failure tells you which half broke.
|
|
30
|
-
* **
|
|
31
|
-
14-case tamper matrix where
|
|
32
|
-
|
|
39
|
+
* **38 envelope checks** -- 16 rule-layer checks that need no primitive, plus
|
|
40
|
+
round-trip, self-certification, freshness and a 14-case tamper matrix where
|
|
41
|
+
every alteration must be rejected.
|
|
42
|
+
* **5 zero-dependency checks** -- a static scan proving the core imports nothing
|
|
33
43
|
outside the standard library.
|
|
34
44
|
|
|
35
|
-
Offline. No account. No network. `[PASS]` or it is not.
|
|
45
|
+
Offline. No account. No network. `[PASS]` or it is not. Every vector file shipped here is listed in `vectors.manifest.json` with its byte count and SHA-256, produced by a generator rather than typed by hand - recompute the hash yourself if you prefer.
|
|
36
46
|
|
|
37
47
|
---
|
|
38
48
|
|
|
@@ -46,7 +56,7 @@ pip install rttp[ed25519] # + sovereign-profile seals (Ed25519)
|
|
|
46
56
|
Python 3.9+. No compiled extensions in the core.
|
|
47
57
|
|
|
48
58
|
The core deliberately has **no dependencies**. A protocol reference that cannot be
|
|
49
|
-
read without resolving a dependency tree is not much of a reference
|
|
59
|
+
read without resolving a dependency tree is not much of a reference -- and an
|
|
50
60
|
air-gapped reviewer should be able to check the frames.
|
|
51
61
|
|
|
52
62
|
---
|
|
@@ -55,13 +65,13 @@ air-gapped reviewer should be able to check the frames.
|
|
|
55
65
|
|
|
56
66
|
| Module | What it does | Authority |
|
|
57
67
|
|:---|:---|:---|
|
|
58
|
-
| `rttp.pulse_header` | `PulseHeader128` codec
|
|
59
|
-
| `rttp.rttp_uri` | Validate, canonicalise and derive `ROUTE_SHARD` from an `rttp` URI | RFC-002
|
|
60
|
-
| `rttp.seal` | **Managed profile**
|
|
61
|
-
| `rttp.seal_asym` | **Sovereign profile**
|
|
68
|
+
| `rttp.pulse_header` | `PulseHeader128` codec -- build / parse / verify the 128-byte hardware-aligned header, including the v1.2.6 extension block at `0x66`-`0x7F` | RFC-002 sec. 4.1 + `SPEC/RTTP-FRAME-EXT-v1.2.6.md` |
|
|
69
|
+
| `rttp.rttp_uri` | Validate, canonicalise and derive `ROUTE_SHARD` from an `rttp` URI | RFC-002 sec. 10.2 / sec. 10.3 ABNF |
|
|
70
|
+
| `rttp.seal` | **Managed profile** -- symmetric HMAC-SHA256 over a pre-shared key table | -- |
|
|
71
|
+
| `rttp.seal_asym` | **Sovereign profile** -- Ed25519, self-certifying, no issuance and no registry | `SPEC/RTTP-SEAL-ENVELOPE-v1.2.6.md` (draft) |
|
|
62
72
|
| `rttp.aid` | Autonomous Identity derivation | RFC-001 |
|
|
63
73
|
|
|
64
|
-
Plus `rttp.vectors`
|
|
74
|
+
Plus `rttp.vectors` -- the published conformance vectors, shipped inside the wheel
|
|
65
75
|
so the self-test works from an installed package with no repository checkout.
|
|
66
76
|
|
|
67
77
|
---
|
|
@@ -79,9 +89,9 @@ parsed["action"] # 'verify'
|
|
|
79
89
|
parsed["route_shard"].hex() # 16-byte routing hash, pure computation
|
|
80
90
|
```
|
|
81
91
|
|
|
82
|
-
Addressing is **DNS-free** (RFC-002
|
|
92
|
+
Addressing is **DNS-free** (RFC-002 sec. 10.5): the routing hash is derived from the
|
|
83
93
|
authority by SHA-256, so no registry, resolver or network is involved. Malformed
|
|
84
|
-
input is rejected rather than normalised
|
|
94
|
+
input is rejected rather than normalised -- a case variant is not a spelling
|
|
85
95
|
difference, it is a different string.
|
|
86
96
|
|
|
87
97
|
### Build a frame from a URI
|
|
@@ -99,9 +109,9 @@ len(raw) # 128
|
|
|
99
109
|
pulse_header.verify(raw)["action"] # 'verify'
|
|
100
110
|
```
|
|
101
111
|
|
|
102
|
-
`build_for_uri` performs the mapping RFC-002
|
|
112
|
+
`build_for_uri` performs the mapping RFC-002 sec. 10.4 promises: the URI's authority
|
|
103
113
|
becomes `ROUTE_SHARD`, its path becomes `ACTION`. Readers that only understand
|
|
104
|
-
`0x00
|
|
114
|
+
`0x00`-`0x65` keep working -- the extension block occupies bytes that were already
|
|
105
115
|
zero, and `VERSION_ID` stays 130.
|
|
106
116
|
|
|
107
117
|
### Seal with your own identity
|
|
@@ -125,7 +135,7 @@ credentials to obtain, and no operator who could refuse to issue them.
|
|
|
125
135
|
|
|
126
136
|
## Two profiles, and when each applies
|
|
127
137
|
|
|
128
|
-
| | **Managed**
|
|
138
|
+
| | **Managed** -- `seal` | **Sovereign** -- `seal_asym` |
|
|
129
139
|
|:---|:---|:---|
|
|
130
140
|
| Primitive | HMAC-SHA256 | Ed25519 |
|
|
131
141
|
| Key material | pre-shared table | self-generated |
|
|
@@ -134,7 +144,7 @@ credentials to obtain, and no operator who could refuse to issue them.
|
|
|
134
144
|
| A stranger can participate | no | **yes** |
|
|
135
145
|
| One leaked key | can forge any peer | forges one identity |
|
|
136
146
|
|
|
137
|
-
Inside a set of symmetric secrets there is no such thing as a stranger
|
|
147
|
+
Inside a set of symmetric secrets there is no such thing as a stranger -- whoever
|
|
138
148
|
holds the shared key can mint anybody's seal. The managed profile is therefore for
|
|
139
149
|
a closed organism, where a fixed set of roles genuinely do trust each other.
|
|
140
150
|
|
|
@@ -143,19 +153,19 @@ preference; it is the difference between a private system and a public one.
|
|
|
143
153
|
|
|
144
154
|
---
|
|
145
155
|
|
|
146
|
-
## Scope
|
|
156
|
+
## Scope -- what this package is not
|
|
147
157
|
|
|
148
158
|
| | |
|
|
149
159
|
|:---|:---|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
160
|
+
| OK **Framing** | Real, and specified. |
|
|
161
|
+
| OK **Addressing** | Real, and specified. |
|
|
162
|
+
| OK **Sealing** | Real, and specified (draft). |
|
|
163
|
+
| NO **Transport** | **Not here.** This package encodes, addresses and seals. It does not open sockets, and there is no `send()` -- a client and its transport belong to a separate package, so that the codec stays dependency-free and auditable. |
|
|
164
|
+
| NO **Routing service** | Not here. `ROUTE_SHARD` is computed locally; delivering a frame to that hash is an operator's job, not the codec's. |
|
|
165
|
+
| NO **Confidentiality** | Signing is not encryption. |
|
|
156
166
|
|
|
157
167
|
**The seal is an envelope around the frame, not bytes inside it.** A reader that
|
|
158
|
-
parses only the 128-byte header sees no seal at all
|
|
168
|
+
parses only the 128-byte header sees no seal at all -- which is why the two
|
|
159
169
|
concerns are specified separately.
|
|
160
170
|
|
|
161
171
|
Unknown revisions, algorithms and malformed input **fail closed** everywhere.
|
|
@@ -166,7 +176,7 @@ Nothing in this package is ever accepted because a check could not be performed.
|
|
|
166
176
|
## Conformance vectors
|
|
167
177
|
|
|
168
178
|
`rttp/vectors/rttp-conformance-v1.2.6.json` is generated, not hand-edited. Each
|
|
169
|
-
vector is deterministic: fixed sequence numbers, fixed timestamp, fixed AID
|
|
179
|
+
vector is deterministic: fixed sequence numbers, fixed timestamp, fixed AID -- so
|
|
170
180
|
two implementations either agree byte for byte or they do not.
|
|
171
181
|
|
|
172
182
|
The vector set is the intended deliverable for third-party certification: pass it
|
|
@@ -180,21 +190,21 @@ needing to trust the authors.
|
|
|
180
190
|
| Ecosystem | Name | Status |
|
|
181
191
|
|:---|:---|:---|
|
|
182
192
|
| crates.io | `rttp` | held by this project |
|
|
183
|
-
| PyPI | `rttp` | **this package**
|
|
184
|
-
| npm | **`@aicent/rttp`** | the unscoped `rttp` name on npm is held by an unrelated, unmaintained 2017 REST helper, and the `rttp` scope cannot be created at all
|
|
193
|
+
| PyPI | `rttp` | **this package** -- `pip install rttp` |
|
|
194
|
+
| npm | **`@aicent/rttp`** | the unscoped `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 -- so the JavaScript side is published under this project's own organization. The package name itself is still just `rttp` |
|
|
185
195
|
|
|
186
196
|
---
|
|
187
197
|
|
|
188
198
|
## Specification status
|
|
189
199
|
|
|
190
|
-
* **`rttp` URI scheme**
|
|
200
|
+
* **`rttp` URI scheme** -- submitted to IANA under RFC 7595, ticket **#1459939**,
|
|
191
201
|
Provisional (First Come, First Served), **under review**. It is **not yet
|
|
192
|
-
registered**
|
|
202
|
+
registered** -- as of 2026-09-15 the IANA URI Schemes registry contains no
|
|
193
203
|
`rttp` entry. Please describe it that way.
|
|
194
|
-
* **Frame layout**
|
|
195
|
-
* **Seal envelope**
|
|
204
|
+
* **Frame layout** -- RFC-002 sec. 4.1, extended by `SPEC/RTTP-FRAME-EXT-v1.2.6.md`.
|
|
205
|
+
* **Seal envelope** -- `SPEC/RTTP-SEAL-ENVELOPE-v1.2.6.md`, a **draft**: the format
|
|
196
206
|
is implemented and vector-tested, but it is not yet a numbered RFC-002 section.
|
|
197
|
-
* **Managed profile**
|
|
207
|
+
* **Managed profile** -- implementation only; it predates the sovereign profile and
|
|
198
208
|
has no standalone specification.
|
|
199
209
|
|
|
200
210
|
Where this package and a specification disagree, **the specification wins and the
|