Skip to content

Commit e3eb886

Browse files
Implement Megatorrent Reference (Validation Fixes + Monorepo + Handoff)
1 parent 295c69a commit e3eb886

File tree

13 files changed

+728
-1
lines changed

13 files changed

+728
-1
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "qbittorrent"]
2+
path = qbittorrent
3+
url = https://github.com/robertpelloni/qbittorrent

README-monorepo.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Megatorrent Monorepo
2+
3+
This repository contains the reference implementation for the Megatorrent protocol, a decentralized, mutable successor to BitTorrent.
4+
5+
## Structure
6+
7+
* `docs/`: Architecture and Protocol Specifications.
8+
* `tracker/`: Node.js implementation of the Tracker and Reference Client (the root of this repo, historically).
9+
* `qbittorrent/`: C++ Client Fork (Submodule).
10+
11+
## Getting Started
12+
13+
### Node.js Tracker & Reference Client
14+
Run the tracker and client tests:
15+
```bash
16+
npm install
17+
npm test
18+
```
19+
20+
### qBittorrent Client
21+
See `qbittorrent/README.md` for C++ build instructions.

docs/ARCHITECTURE.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
## 6. Obfuscated Storage Protocol (Phase 2)
3+
To achieve plausible deniability and distributed redundancy, data is not stored as "files" but as "Blobs" of high-entropy data.
4+
5+
### Concepts
6+
* **Blob**: A fixed-size (or variable) container stored by a Host. To the Host, it is just random bytes.
7+
* **Chunk**: A segment of a user's file.
8+
* **Encryption**: Each Chunk is encrypted with a *unique* random key (ChaCha20-Poly1305) before being placed into a Blob.
9+
* **Muxing**: A Blob may contain multiple Chunks (potentially from different files) or padding.
10+
11+
### Data Structure: The `FileEntry`
12+
This structure replaces the simple "magnet link" in the Megatorrent Manifest.
13+
14+
```json
15+
{
16+
"name": "episode1.mkv",
17+
"mime": "video/x-matroska",
18+
"size": 104857600,
19+
"chunks": [
20+
{
21+
"blobId": "<SHA256 Hash of the Blob>",
22+
"offset": 0, // Byte offset in the Blob
23+
"length": 1048576, // Length of the encrypted chunk
24+
"key": "<32-byte Hex Key>",
25+
"nonce": "<12-byte Hex Nonce>",
26+
"authTag": "<16-byte Hex Tag>"
27+
},
28+
...
29+
]
30+
}
31+
```
32+
33+
### Process
34+
1. **Ingest**:
35+
* File is split into N chunks.
36+
* For each chunk:
37+
* Generate random Key & Nonce.
38+
* Encrypt Chunk -> EncryptedChunk.
39+
* (Simplification for Ref Impl) EncryptedChunk becomes a "Blob" directly (or is wrapped).
40+
* Blob ID = SHA256(Blob).
41+
* Result: A list of Blobs (to be uploaded) and a `FileEntry` (to be put in the Manifest).
42+
43+
2. **Access**:
44+
* Subscriber receives Manifest.
45+
* Parses `FileEntry`.
46+
* Requests Blob(ID) from the network (simulated via local dir or tracker relay).
47+
* Extracts bytes at `offset` for `length`.
48+
* Decrypts using `key` and `nonce`.
49+
* Reassembles file.

lib/manifest.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import stringify from 'fast-json-stable-stringify'
2+
import sodium from 'sodium-native'
3+
4+
export function verify (message, signature, publicKey) {
5+
const msgBuffer = Buffer.isBuffer(message) ? message : Buffer.from(message)
6+
return sodium.crypto_sign_verify_detached(signature, msgBuffer, publicKey)
7+
}
8+
9+
export function validateManifest (manifest) {
10+
if (!manifest || typeof manifest !== 'object') throw new Error('Invalid manifest')
11+
if (!manifest.publicKey || !manifest.signature) throw new Error('Missing keys')
12+
13+
// Validation
14+
if (typeof manifest.publicKey !== 'string' || !/^[0-9a-fA-F]{64}$/.test(manifest.publicKey)) {
15+
throw new Error('Invalid public key format')
16+
}
17+
if (typeof manifest.signature !== 'string' || !/^[0-9a-fA-F]{128}$/.test(manifest.signature)) {
18+
throw new Error('Invalid signature format')
19+
}
20+
if (typeof manifest.sequence !== 'number') throw new Error('Invalid sequence')
21+
if (typeof manifest.timestamp !== 'number') throw new Error('Invalid timestamp')
22+
if (!Array.isArray(manifest.collections)) throw new Error('Invalid collections')
23+
24+
// Reconstruct the payload to verify (exclude signature)
25+
const payload = {
26+
publicKey: manifest.publicKey,
27+
sequence: manifest.sequence,
28+
timestamp: manifest.timestamp,
29+
collections: manifest.collections
30+
}
31+
32+
const jsonString = stringify(payload)
33+
const publicKey = Buffer.from(manifest.publicKey, 'hex')
34+
const signature = Buffer.from(manifest.signature, 'hex')
35+
36+
return verify(jsonString, signature, publicKey)
37+
}

lib/server/parse-websocket.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export default function (socket, opts, params) {
4848
return bin2hex(binaryInfoHash)
4949
})
5050
}
51+
} else if (params.action === 'publish') {
52+
// Custom Action: Publish Manifest
53+
if (!params.manifest) throw new Error('missing manifest')
54+
} else if (params.action === 'subscribe') {
55+
// Custom Action: Subscribe to Key
56+
if (!params.key) throw new Error('missing key')
5157
} else {
5258
throw new Error(`invalid action in WS request: ${params.action}`)
5359
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"compact2string": "^1.4.1",
3636
"cross-fetch-ponyfill": "^1.0.3",
3737
"debug": "^4.3.4",
38+
"fast-json-stable-stringify": "^2.1.0",
3839
"ip": "^2.0.1",
3940
"lru": "^3.1.0",
4041
"minimist": "^1.2.8",
@@ -44,6 +45,7 @@
4445
"run-parallel": "^1.2.0",
4546
"run-series": "^1.1.9",
4647
"socks": "^2.8.3",
48+
"sodium-native": "^5.0.10",
4749
"string2compact": "^2.0.1",
4850
"uint8-util": "^2.2.5",
4951
"unordered-array-remove": "^1.0.2",
@@ -52,7 +54,7 @@
5254
"devDependencies": {
5355
"@mapbox/node-pre-gyp": "1.0.11",
5456
"@webtorrent/semantic-release-config": "1.0.10",
55-
"magnet-uri": "7.0.7",
57+
"magnet-uri": "^7.0.7",
5658
"semantic-release": "21.1.2",
5759
"standard": "*",
5860
"tape": "5.9.0",

qbittorrent

Submodule qbittorrent added at 5abf458

0 commit comments

Comments
 (0)