yakmesh 1.3.1 β†’ 1.4.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to YAKMESH will be documented in this file.
4
4
 
5
+ ## [1.3.2] - 2026-01-17
6
+
7
+ ### Added
8
+ - **Public Content Delivery API** - Content-addressed storage for decentralized website hosting
9
+ - `GET /content` - List available content with stats
10
+ - `GET /content/:hash` - Fetch content by hash with optional proof
11
+ - `POST /content` - Publish content with consensus verification
12
+ - Content gossip via mesh for cross-node synchronization
13
+ - Consensus proof system for verified content
14
+
15
+ ### Fixed
16
+ - Gossip protocol method calls (use `spreadRumor()` instead of `broadcast()`)
17
+ - Direct messaging via mesh instead of non-existent gossip.sendTo()
18
+
19
+ ### Community
20
+ - Added social links: Discord, Telegram, X (Twitter)
21
+ - Created Discord announcement template
22
+
23
+ ---
24
+
5
25
  ## [1.3.1] - 2026-01-16
6
26
 
7
27
  ### Security
package/README.md CHANGED
@@ -179,6 +179,12 @@ See [TRADEMARK.md](TRADEMARK.md) for trademark usage policy.
179
179
  <br><br>
180
180
  <strong><a href="https://yakmesh.dev">yakmesh.dev</a></strong>
181
181
  <br><br>
182
+ <p>
183
+ <a href="https://discord.gg/E62tAE2wGh">πŸ’¬ Discord</a> β€’
184
+ <a href="https://t.me/yakmesh">πŸ“± Telegram</a> β€’
185
+ <a href="https://x.com/yakmesh_dev">𝕏 Twitter</a>
186
+ </p>
187
+ <br>
182
188
  <sub>Β© 2026 YAKMESHβ„’ Project. Sturdy & Secure.</sub>
183
189
  <br>
184
190
  <sub>YAKMESHβ„’ is a trademark of PeerQuanta, application pending (Serial No. 99594620).</sub>
@@ -0,0 +1,55 @@
1
+ # 🦬 YAKMESH v1.3.1 β€” Public Content Delivery + Mesh Peering Confirmed!
2
+
3
+ Hey everyone! Big update today:
4
+
5
+ ## βœ… What's New
6
+
7
+ ### 🌐 Public Content Delivery API
8
+ We've added a complete **content-addressed storage system** with public delivery:
9
+
10
+ ```
11
+ GET /content/:hash β†’ Fetch any content by its hash
12
+ GET /content/:hash/proof β†’ Get consensus proof for verification
13
+ POST /content/publish β†’ Store and gossip content to mesh
14
+ ```
15
+
16
+ **Key features:**
17
+ - Content addressed by SHA3-256 hash (trustless verification)
18
+ - Consensus proofs for light client verification
19
+ - LRU caching for instant edge delivery
20
+ - Automatic mesh sync via gossip protocol
21
+
22
+ ### πŸ”— First Successful LAN Mesh Peering
23
+ Tested and confirmed: **two Yakmesh nodes successfully peered** with matching network fingerprints. The Code Proof Protocol verified both were running identical codebases before allowing the connection.
24
+
25
+ **Connection is as simple as:**
26
+ ```powershell
27
+ POST http://localhost:3000/connect
28
+ { "address": "ws://192.168.1.178:9001" }
29
+ ```
30
+
31
+ ### πŸ“± New Social Channels
32
+ We're now on:
33
+ - πŸ’¬ **Discord**: https://discord.gg/E62tAE2wGh
34
+ - πŸ“± **Telegram**: https://t.me/yakmesh
35
+ - 𝕏 **Twitter**: https://x.com/yakmesh
36
+
37
+ ## πŸ“¦ Install
38
+
39
+ ```bash
40
+ npm install yakmesh@1.3.1
41
+ ```
42
+
43
+ ## πŸ”— Links
44
+ - 🌐 Website: https://yakmesh.dev
45
+ - πŸ“– GitHub: https://github.com/yakmesh/yakmesh
46
+ - πŸ“¦ npm: https://npmjs.com/package/yakmesh
47
+
48
+ ---
49
+
50
+ **What's next?**
51
+ - Multi-node cluster testing
52
+ - Production deployment
53
+ - Website/webapp hosting demos
54
+
55
+ Questions? Drop them here! 🦬
package/content/api.js ADDED
@@ -0,0 +1,348 @@
1
+ /**
2
+ * YAKMESHβ„’ Public Content API
3
+ * HTTP endpoints for public content delivery
4
+ *
5
+ * Public (no auth required):
6
+ * - GET /content/:hash - Fetch content by hash
7
+ * - GET /content/:hash/meta - Fetch metadata only
8
+ * - GET /content/:hash/proof - Fetch consensus proof
9
+ * - GET /content/list - List available content
10
+ *
11
+ * Authenticated (rate limited):
12
+ * - POST /content/publish - Publish new content
13
+ * - DELETE /content/:hash - Remove content (owner only)
14
+ *
15
+ * @module content/api
16
+ * @license MIT
17
+ * @copyright 2026 YAKMESH Contributors
18
+ */
19
+
20
+ import { Router } from 'express';
21
+ import { ContentStore, ContentType, ContentStatus, computeContentHash } from './store.js';
22
+
23
+ /**
24
+ * Create content API router
25
+ */
26
+ export function createContentAPI(contentStore, options = {}) {
27
+ const router = Router();
28
+
29
+ const {
30
+ writeLimiter,
31
+ readLimiter,
32
+ validateString,
33
+ } = options;
34
+
35
+ // =========================================
36
+ // PUBLIC READ ENDPOINTS (No Auth)
37
+ // =========================================
38
+
39
+ /**
40
+ * GET /content/:hash
41
+ * Fetch content by hash with optional proof
42
+ *
43
+ * Query params:
44
+ * - proof=1 : Include consensus proof in response headers
45
+ * - download=1 : Force download (Content-Disposition)
46
+ */
47
+ router.get('/:hash', readLimiter, (req, res) => {
48
+ const { hash } = req.params;
49
+ const includeProof = req.query.proof === '1';
50
+ const download = req.query.download === '1';
51
+
52
+ // Get content with metadata
53
+ const result = contentStore.getWithProof(hash);
54
+
55
+ if (!result) {
56
+ return res.status(404).json({
57
+ error: 'Content not found',
58
+ hash,
59
+ hint: 'Content may not have synced yet. Try again later.',
60
+ });
61
+ }
62
+
63
+ // Set content type
64
+ res.setHeader('Content-Type', result.meta?.contentType || 'application/octet-stream');
65
+ res.setHeader('Content-Length', result.meta?.size || result.content.length);
66
+ res.setHeader('X-Content-Hash', result.hash);
67
+ res.setHeader('X-Content-Status', result.meta?.status || 'unknown');
68
+
69
+ // Cache headers (immutable content = cache forever)
70
+ if (result.verified) {
71
+ res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
72
+ } else {
73
+ res.setHeader('Cache-Control', 'public, max-age=60');
74
+ }
75
+
76
+ // Include proof in headers if requested
77
+ if (includeProof && result.proof) {
78
+ res.setHeader('X-Consensus-Proof', JSON.stringify(result.proof));
79
+ res.setHeader('X-Verified', result.verified ? 'true' : 'false');
80
+ }
81
+
82
+ // Download disposition
83
+ if (download && result.meta?.name) {
84
+ res.setHeader('Content-Disposition', `attachment; filename="${result.meta.name}"`);
85
+ }
86
+
87
+ // Send content
88
+ res.send(result.content);
89
+ });
90
+
91
+ /**
92
+ * GET /content/:hash/meta
93
+ * Fetch metadata only (no content body)
94
+ */
95
+ router.get('/:hash/meta', readLimiter, (req, res) => {
96
+ const { hash } = req.params;
97
+ const meta = contentStore.getMeta(hash);
98
+
99
+ if (!meta) {
100
+ return res.status(404).json({ error: 'Content not found', hash });
101
+ }
102
+
103
+ res.json(meta.toJSON ? meta.toJSON() : meta);
104
+ });
105
+
106
+ /**
107
+ * GET /content/:hash/proof
108
+ * Fetch consensus proof for light client verification
109
+ */
110
+ router.get('/:hash/proof', readLimiter, (req, res) => {
111
+ const { hash } = req.params;
112
+ const meta = contentStore.getMeta(hash);
113
+
114
+ if (!meta) {
115
+ return res.status(404).json({ error: 'Content not found', hash });
116
+ }
117
+
118
+ if (!meta.consensusProof) {
119
+ return res.status(404).json({
120
+ error: 'No consensus proof yet',
121
+ hash,
122
+ status: meta.status,
123
+ hint: 'Content may still be pending consensus.',
124
+ });
125
+ }
126
+
127
+ res.json({
128
+ hash,
129
+ verified: meta.status === ContentStatus.VERIFIED,
130
+ proof: meta.consensusProof.toJSON ? meta.consensusProof.toJSON() : meta.consensusProof,
131
+ });
132
+ });
133
+
134
+ /**
135
+ * GET /content/list
136
+ * List available content
137
+ *
138
+ * Query params:
139
+ * - tag=<tag> : Filter by tag
140
+ * - status=<status> : Filter by status (local, pending, verified)
141
+ * - limit=<n> : Max results (default 100)
142
+ * - offset=<n> : Pagination offset
143
+ */
144
+ router.get('/', readLimiter, (req, res) => {
145
+ const { tag, status, limit = 100, offset = 0 } = req.query;
146
+
147
+ const items = contentStore.list({
148
+ tag,
149
+ status,
150
+ limit: Math.min(parseInt(limit) || 100, 1000),
151
+ offset: parseInt(offset) || 0,
152
+ });
153
+
154
+ res.json({
155
+ items,
156
+ count: items.length,
157
+ stats: contentStore.getStats(),
158
+ });
159
+ });
160
+
161
+ /**
162
+ * HEAD /content/:hash
163
+ * Check if content exists (useful for CDN/cache validation)
164
+ */
165
+ router.head('/:hash', readLimiter, (req, res) => {
166
+ const { hash } = req.params;
167
+
168
+ if (contentStore.has(hash)) {
169
+ const meta = contentStore.getMeta(hash);
170
+ res.setHeader('Content-Type', meta?.contentType || 'application/octet-stream');
171
+ res.setHeader('Content-Length', meta?.size || 0);
172
+ res.setHeader('X-Content-Hash', hash);
173
+ res.setHeader('X-Content-Status', meta?.status || 'unknown');
174
+ res.status(200).end();
175
+ } else {
176
+ res.status(404).end();
177
+ }
178
+ });
179
+
180
+ // =========================================
181
+ // AUTHENTICATED WRITE ENDPOINTS
182
+ // =========================================
183
+
184
+ /**
185
+ * POST /content/publish
186
+ * Publish new content to the mesh
187
+ *
188
+ * Body (JSON):
189
+ * {
190
+ * content: <string|object>,
191
+ * contentType?: <mime-type>,
192
+ * name?: <human-readable-name>,
193
+ * tags?: [<tag>, ...],
194
+ * ttl?: <seconds>
195
+ * }
196
+ *
197
+ * Body (multipart/form-data):
198
+ * - file: uploaded file
199
+ * - name: optional name
200
+ * - tags: comma-separated tags
201
+ */
202
+ router.post('/publish', writeLimiter, async (req, res) => {
203
+ try {
204
+ let content;
205
+ let options = {};
206
+
207
+ // Handle JSON body
208
+ if (req.is('application/json')) {
209
+ if (!req.body.content) {
210
+ return res.status(400).json({ error: 'Content required' });
211
+ }
212
+ content = req.body.content;
213
+ options = {
214
+ contentType: req.body.contentType,
215
+ name: req.body.name,
216
+ tags: req.body.tags || [],
217
+ ttl: req.body.ttl || 0,
218
+ };
219
+ }
220
+ // Handle raw body
221
+ else if (req.body && Buffer.isBuffer(req.body)) {
222
+ content = req.body;
223
+ options = {
224
+ contentType: req.headers['content-type'] || 'application/octet-stream',
225
+ name: req.headers['x-content-name'],
226
+ tags: req.headers['x-content-tags']?.split(',') || [],
227
+ };
228
+ }
229
+ // Handle form data (basic - for full multipart use multer)
230
+ else if (req.body?.content) {
231
+ content = req.body.content;
232
+ options = {
233
+ contentType: req.body.contentType,
234
+ name: req.body.name,
235
+ tags: typeof req.body.tags === 'string' ? req.body.tags.split(',') : req.body.tags,
236
+ };
237
+ }
238
+ else {
239
+ return res.status(400).json({ error: 'Content required in body' });
240
+ }
241
+
242
+ // Store and publish
243
+ const result = await contentStore.store(content, options);
244
+
245
+ res.status(201).json({
246
+ success: true,
247
+ hash: result.hash,
248
+ status: result.status,
249
+ meta: result.meta?.toJSON ? result.meta.toJSON() : result.meta,
250
+ url: `/content/${result.hash}`,
251
+ });
252
+ } catch (error) {
253
+ res.status(500).json({ error: error.message });
254
+ }
255
+ });
256
+
257
+ /**
258
+ * POST /content/request
259
+ * Request content from mesh (if not locally available)
260
+ */
261
+ router.post('/request', writeLimiter, async (req, res) => {
262
+ const { hash } = req.body;
263
+
264
+ if (!hash) {
265
+ return res.status(400).json({ error: 'Hash required' });
266
+ }
267
+
268
+ try {
269
+ // Check local first
270
+ if (contentStore.has(hash)) {
271
+ return res.json({
272
+ found: true,
273
+ local: true,
274
+ hash,
275
+ });
276
+ }
277
+
278
+ // Request from mesh
279
+ const result = await contentStore.request(hash);
280
+
281
+ res.json({
282
+ found: true,
283
+ local: false,
284
+ hash: result.hash,
285
+ meta: result.meta,
286
+ });
287
+ } catch (error) {
288
+ res.status(404).json({
289
+ found: false,
290
+ hash,
291
+ error: error.message,
292
+ });
293
+ }
294
+ });
295
+
296
+ /**
297
+ * DELETE /content/:hash
298
+ * Remove content (local only - cannot remove from mesh)
299
+ */
300
+ router.delete('/:hash', writeLimiter, (req, res) => {
301
+ const { hash } = req.params;
302
+
303
+ if (!contentStore.has(hash)) {
304
+ return res.status(404).json({ error: 'Content not found', hash });
305
+ }
306
+
307
+ // Note: This only removes locally - content may still exist on other nodes
308
+ contentStore.delete(hash);
309
+
310
+ res.json({
311
+ deleted: true,
312
+ hash,
313
+ note: 'Content removed locally. Other mesh nodes may still have copies.',
314
+ });
315
+ });
316
+
317
+ /**
318
+ * GET /content/stats
319
+ * Get content store statistics
320
+ */
321
+ router.get('/stats', readLimiter, (req, res) => {
322
+ res.json(contentStore.getStats());
323
+ });
324
+
325
+ /**
326
+ * POST /content/verify
327
+ * Compute hash for content without storing
328
+ * Useful for clients to verify content integrity
329
+ */
330
+ router.post('/verify', readLimiter, (req, res) => {
331
+ const content = req.body.content || req.body;
332
+
333
+ if (!content) {
334
+ return res.status(400).json({ error: 'Content required' });
335
+ }
336
+
337
+ const hash = computeContentHash(content);
338
+
339
+ res.json({
340
+ hash,
341
+ exists: contentStore.has(hash),
342
+ });
343
+ });
344
+
345
+ return router;
346
+ }
347
+
348
+ export default createContentAPI;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * YAKMESHβ„’ Content Module
3
+ * Content-addressed storage with public delivery
4
+ *
5
+ * @module content
6
+ * @license MIT
7
+ * @copyright 2026 YAKMESH Contributors
8
+ */
9
+
10
+ export {
11
+ ContentStore,
12
+ ContentType,
13
+ ContentStatus,
14
+ ContentMetadata,
15
+ ConsensusProof,
16
+ computeContentHash,
17
+ } from './store.js';
18
+
19
+ export { createContentAPI } from './api.js';