yakmesh 2.9.0 → 3.0.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 +637 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/content/api.js +50 -41
- package/content/index.js +1 -2
- package/content/store.js +323 -177
- package/dashboard/index.html +19 -3
- package/database/replication.js +117 -37
- package/docs/CRYPTO-AGILITY.md +204 -0
- package/docs/MTLS-RESEARCH.md +367 -0
- package/docs/NAMCHE-SPEC.md +681 -0
- package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
- package/docs/PRECISION-DISCLOSURE.md +96 -0
- package/docs/README.md +76 -0
- package/docs/ROADMAP-2.4.0.md +447 -0
- package/docs/ROADMAP-2.5.0.md +244 -0
- package/docs/SECURITY-AUDIT-REPORT.md +306 -0
- package/docs/SST-INTEGRATION.md +712 -0
- package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
- package/docs/TERNARY-AUDIT-REPORT.md +247 -0
- package/docs/TME-FAQ.md +221 -0
- package/docs/WHITEPAPER.md +623 -0
- package/docs/adapters.html +1001 -0
- package/docs/advanced-systems.html +1045 -0
- package/docs/annex.html +1046 -0
- package/docs/api.html +970 -0
- package/docs/business/response-templates.md +160 -0
- package/docs/c2c.html +1225 -0
- package/docs/cli.html +1332 -0
- package/docs/configuration.html +1248 -0
- package/docs/darshan.html +1085 -0
- package/docs/dharma.html +966 -0
- package/docs/docs-bundle.html +1075 -0
- package/docs/docs.css +3120 -0
- package/docs/docs.js +556 -0
- package/docs/doko.html +969 -0
- package/docs/geo-proof.html +858 -0
- package/docs/getting-started.html +840 -0
- package/docs/gumba-tutorial.html +1144 -0
- package/docs/gumba.html +1098 -0
- package/docs/index.html +914 -0
- package/docs/jhilke.html +1312 -0
- package/docs/karma.html +1100 -0
- package/docs/katha.html +1037 -0
- package/docs/lama.html +978 -0
- package/docs/mandala.html +1067 -0
- package/docs/mani.html +964 -0
- package/docs/mantra.html +967 -0
- package/docs/mesh.html +1409 -0
- package/docs/nakpak.html +869 -0
- package/docs/namche.html +928 -0
- package/docs/nav-order.json +53 -0
- package/docs/prahari.html +1043 -0
- package/docs/prism-bash.min.js +1 -0
- package/docs/prism-javascript.min.js +1 -0
- package/docs/prism-json.min.js +1 -0
- package/docs/prism-tomorrow.min.css +1 -0
- package/docs/prism.min.js +1 -0
- package/docs/privacy.html +699 -0
- package/docs/quick-reference.html +1181 -0
- package/docs/sakshi.html +1402 -0
- package/docs/sandboxing.md +386 -0
- package/docs/seva.html +911 -0
- package/docs/sherpa.html +871 -0
- package/docs/studio.html +860 -0
- package/docs/stupa.html +995 -0
- package/docs/tailwind.min.css +2 -0
- package/docs/tattva.html +1332 -0
- package/docs/terms.html +686 -0
- package/docs/time-server-deployment.md +166 -0
- package/docs/time-sources.html +1392 -0
- package/docs/tivra.html +1127 -0
- package/docs/trademark-policy.html +686 -0
- package/docs/tribhuj.html +1183 -0
- package/docs/trust-security.html +1029 -0
- package/docs/tutorials/backup-recovery.html +654 -0
- package/docs/tutorials/dashboard.html +604 -0
- package/docs/tutorials/domain-setup.html +605 -0
- package/docs/tutorials/host-website.html +456 -0
- package/docs/tutorials/mesh-network.html +505 -0
- package/docs/tutorials/mobile-access.html +445 -0
- package/docs/tutorials/privacy.html +467 -0
- package/docs/tutorials/raspberry-pi.html +600 -0
- package/docs/tutorials/security-basics.html +539 -0
- package/docs/tutorials/share-files.html +431 -0
- package/docs/tutorials/troubleshooting.html +637 -0
- package/docs/tutorials/trust-karma.html +419 -0
- package/docs/tutorials/yak-protocol.html +456 -0
- package/docs/tutorials.html +1034 -0
- package/docs/vani.html +1270 -0
- package/docs/webserver.html +809 -0
- package/docs/yak-protocol.html +940 -0
- package/docs/yak-timeserver-design.md +475 -0
- package/docs/yakapp.html +1015 -0
- package/docs/ypc27.html +1069 -0
- package/docs/yurt.html +1344 -0
- package/embedded-docs/bundle.js +274 -114
- package/gossip/protocol.js +247 -27
- package/identity/key-resolver.js +262 -0
- package/identity/machine-seed.js +632 -0
- package/identity/node-key.js +669 -368
- package/identity/tribhuj-ratchet.js +506 -0
- package/knowledge-base.js +37 -8
- package/launcher/yakmesh.bat +62 -0
- package/launcher/yakmesh.sh +70 -0
- package/mesh/annex.js +462 -108
- package/mesh/beacon-broadcast.js +4 -1
- package/mesh/darshan.js +17 -5
- package/mesh/gumba.js +47 -13
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +5 -2
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/seva.js +526 -0
- package/mesh/sherpa-discovery.js +89 -8
- package/mesh/sybil-defense.js +19 -5
- package/mesh/temporal-encoder.js +4 -3
- package/mesh/yurt.js +72 -17
- package/models/entropy-sentinel.onnx +0 -0
- package/models/karma-trust.onnx +0 -0
- package/models/manifest.json +43 -0
- package/models/sakshi-anomaly.onnx +0 -0
- package/oracle/code-proof-protocol.js +7 -6
- package/oracle/codebase-lock.js +257 -28
- package/oracle/index.js +74 -15
- package/oracle/ma902-snmp.js +678 -0
- package/oracle/module-sealer.js +5 -3
- package/oracle/packet-checksum.js +201 -0
- package/oracle/ternary-144t.js +714 -0
- package/oracle/ternary-ml.js +481 -0
- package/oracle/time-api.js +239 -0
- package/oracle/time-source.js +137 -47
- package/oracle/validation-oracle-hardened.js +1111 -1071
- package/oracle/validation-oracle.js +4 -2
- package/oracle/ypc27.js +211 -0
- package/package.json +20 -3
- package/protocol/yak-handler.js +35 -9
- package/protocol/yak-protocol.js +6 -5
- package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
- package/reference/cpp/yakmesh_ypc27.cpp +179 -0
- package/sbom.json +87 -0
- package/scripts/security-audit.mjs +264 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +4 -3
- package/security/doko-identity.js +193 -143
- package/security/domain-consensus.js +86 -85
- package/security/fs-hardening.js +620 -0
- package/security/hardware-attestation.js +5 -3
- package/security/hybrid-trust.js +227 -87
- package/security/karma-rate-limiter.js +692 -0
- package/security/khata-protocol.js +22 -21
- package/security/khata-trust-integration.js +277 -150
- package/security/memory-safety.js +635 -0
- package/security/mesh-auth.js +11 -10
- package/security/mesh-revocation.js +18 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +102 -3
- package/security/sangha.js +770 -0
- package/security/secure-config.js +473 -0
- package/security/silicon-parity.js +13 -10
- package/security/steadywatch.js +1142 -0
- package/security/strike-system.js +32 -3
- package/security/temporal-signing.js +488 -0
- package/security/trit-commitment.js +464 -0
- package/server/crypto/annex.js +247 -0
- package/server/darshan-api.js +343 -0
- package/server/index.js +3259 -362
- package/server/komm-api.js +668 -0
- package/utils/accel.js +2273 -0
- package/utils/ternary-id.js +79 -0
- package/utils/verify-worker.js +57 -0
- package/webserver/index.js +95 -5
- package/assets/yakmesh-logo.png +0 -0
- package/assets/yakmesh-logo.svg +0 -80
- package/assets/yakmesh-logo2.png +0 -0
- package/assets/yakmesh-logo2sm.png +0 -0
- package/assets/ymsm.png +0 -0
- package/scripts/update-docs-nav.cjs +0 -194
- package/update-docs-nav.cjs +0 -18
- package/update-nav.ps1 +0 -16
- package/website/assets/silhouettes/adapters.svg +0 -107
- package/website/assets/silhouettes/api-endpoints.svg +0 -115
- package/website/assets/silhouettes/atomic-clock.svg +0 -83
- package/website/assets/silhouettes/base-camp.svg +0 -81
- package/website/assets/silhouettes/bridge.svg +0 -69
- package/website/assets/silhouettes/docs-bundle.svg +0 -113
- package/website/assets/silhouettes/doko-basket.svg +0 -70
- package/website/assets/silhouettes/fortress.svg +0 -93
- package/website/assets/silhouettes/gateway.svg +0 -54
- package/website/assets/silhouettes/gears.svg +0 -93
- package/website/assets/silhouettes/globe-satellite.svg +0 -67
- package/website/assets/silhouettes/karma-wheel.svg +0 -137
- package/website/assets/silhouettes/lama-council.svg +0 -141
- package/website/assets/silhouettes/mandala-network.svg +0 -169
- package/website/assets/silhouettes/mani-stones.svg +0 -149
- package/website/assets/silhouettes/mantra-wheel.svg +0 -116
- package/website/assets/silhouettes/mesh-nodes.svg +0 -113
- package/website/assets/silhouettes/nakpak.svg +0 -56
- package/website/assets/silhouettes/peak-lightning.svg +0 -73
- package/website/assets/silhouettes/sherpa.svg +0 -69
- package/website/assets/silhouettes/stupa-tower.svg +0 -119
- package/website/assets/silhouettes/tattva-eye.svg +0 -78
- package/website/assets/silhouettes/terminal.svg +0 -74
- package/website/assets/silhouettes/webserver.svg +0 -145
- package/website/assets/silhouettes/yak.svg +0 -78
- package/website/assets/yakmesh-logo.png +0 -0
- package/website/assets/yakmesh-logo.webp +0 -0
- package/website/assets/yakmesh-logo128x140.webp +0 -0
- package/website/assets/yakmesh-logo2.png +0 -0
- package/website/assets/yakmesh-logo2.svg +0 -51
- package/website/assets/yakmesh-logo40x44.webp +0 -0
- package/website/assets/yakmesh.gif +0 -0
- package/website/assets/yakmesh.ico +0 -0
- package/website/assets/yakmesh.jpg +0 -0
- package/website/assets/yakmesh.pdf +0 -0
- package/website/assets/yakmesh.png +0 -0
- package/website/assets/yakmesh.svg +0 -70
- package/website/assets/yakmesh128.webp +0 -0
- package/website/assets/yakmesh32.png +0 -0
- package/website/assets/yakmesh32.svg +0 -65
- package/website/assets/yakmesh32o.ico +0 -2
- package/website/assets/yakmesh32o.svg +0 -65
- package/website/assets/yakmesh32o.svgz +0 -0
package/mesh/beacon-broadcast.js
CHANGED
|
@@ -32,7 +32,10 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { randomBytes, createHash } from 'crypto';
|
|
35
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
35
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
36
|
+
|
|
37
|
+
// ACCEL: Hardware-accelerated SHA3-256 (OpenSSL/SHA-NI — 4.6x faster)
|
|
38
|
+
import { sha3_256 } from '../utils/accel.js';
|
|
36
39
|
import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
37
40
|
|
|
38
41
|
// YPC-27 quantum-hard checksums for packet integrity
|
package/mesh/darshan.js
CHANGED
|
@@ -37,9 +37,11 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
import { randomBytes, createCipheriv, createDecipheriv, createHash } from 'crypto';
|
|
40
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
40
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
41
41
|
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
42
42
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
43
|
+
// ACCEL: Hardware-accelerated crypto
|
|
44
|
+
import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
|
|
43
45
|
import { createLogger } from '../utils/logger.js';
|
|
44
46
|
import EventEmitter from 'events';
|
|
45
47
|
|
|
@@ -211,6 +213,16 @@ export class DarshanContent {
|
|
|
211
213
|
if (this.path && this.path.length > DARSHAN_CONFIG.maxPathLength) {
|
|
212
214
|
errors.push('path too long');
|
|
213
215
|
}
|
|
216
|
+
// Defense-in-depth: reject path traversal at validation layer too.
|
|
217
|
+
// Note: absolute paths are allowed here because the HOST node legitimately
|
|
218
|
+
// uses them to register content from its own filesystem. The API boundary
|
|
219
|
+
// (darshan-api.js) rejects absolute paths from external requests.
|
|
220
|
+
if (this.path) {
|
|
221
|
+
const normalized = String(this.path).replace(/\\/g, '/');
|
|
222
|
+
if (normalized.includes('..')) {
|
|
223
|
+
errors.push('path traversal not allowed');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
214
226
|
if (this.size > DARSHAN_CONFIG.maxContentSize) {
|
|
215
227
|
errors.push('content exceeds max size');
|
|
216
228
|
}
|
|
@@ -537,7 +549,7 @@ export class DarshanAttestation {
|
|
|
537
549
|
*/
|
|
538
550
|
signAsViewer(secretKey) {
|
|
539
551
|
const payload = utf8ToBytes(this.getSignablePayload());
|
|
540
|
-
const signature =
|
|
552
|
+
const signature = mlDsa65Sign(payload, secretKey);
|
|
541
553
|
this.viewerSignature = bytesToHex(signature);
|
|
542
554
|
return this;
|
|
543
555
|
}
|
|
@@ -548,7 +560,7 @@ export class DarshanAttestation {
|
|
|
548
560
|
signAsHost(secretKey) {
|
|
549
561
|
// Include viewer signature in host signing
|
|
550
562
|
const payload = utf8ToBytes(this.getSignablePayload() + (this.viewerSignature || ''));
|
|
551
|
-
const signature =
|
|
563
|
+
const signature = mlDsa65Sign(payload, secretKey);
|
|
552
564
|
this.hostSignature = bytesToHex(signature);
|
|
553
565
|
return this;
|
|
554
566
|
}
|
|
@@ -563,7 +575,7 @@ export class DarshanAttestation {
|
|
|
563
575
|
const payload = utf8ToBytes(this.getSignablePayload());
|
|
564
576
|
const signatureBytes = hexToBytes(this.viewerSignature);
|
|
565
577
|
const publicKeyBytes = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey;
|
|
566
|
-
return
|
|
578
|
+
return mlDsa65Verify(signatureBytes, payload, publicKeyBytes);
|
|
567
579
|
} catch (err) {
|
|
568
580
|
return false;
|
|
569
581
|
}
|
|
@@ -579,7 +591,7 @@ export class DarshanAttestation {
|
|
|
579
591
|
const payload = utf8ToBytes(this.getSignablePayload() + (this.viewerSignature || ''));
|
|
580
592
|
const signatureBytes = hexToBytes(this.hostSignature);
|
|
581
593
|
const publicKeyBytes = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey;
|
|
582
|
-
return
|
|
594
|
+
return mlDsa65Verify(signatureBytes, payload, publicKeyBytes);
|
|
583
595
|
} catch (err) {
|
|
584
596
|
return false;
|
|
585
597
|
}
|
package/mesh/gumba.js
CHANGED
|
@@ -39,13 +39,16 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import { randomBytes, createCipheriv, createDecipheriv, createHash } from 'crypto';
|
|
42
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
42
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
43
43
|
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
44
44
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
45
|
+
// ACCEL: Hardware-accelerated crypto
|
|
46
|
+
import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
|
|
45
47
|
import { createLogger } from '../utils/logger.js';
|
|
46
48
|
import EventEmitter from 'events';
|
|
47
49
|
|
|
48
50
|
const log = createLogger('mesh:gumba');
|
|
51
|
+
const peerTag = (id) => id?.split('-pq-').pop() || id?.slice?.(-8) || String(id);
|
|
49
52
|
|
|
50
53
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
51
54
|
// CONFIGURATION
|
|
@@ -521,7 +524,7 @@ export class GumbaProof {
|
|
|
521
524
|
|
|
522
525
|
const payloadBytes = utf8ToBytes(payload);
|
|
523
526
|
// API: sign(message, secretKey)
|
|
524
|
-
const signature =
|
|
527
|
+
const signature = mlDsa65Sign(payloadBytes, secretKey);
|
|
525
528
|
|
|
526
529
|
return {
|
|
527
530
|
...challenge,
|
|
@@ -556,7 +559,7 @@ export class GumbaProof {
|
|
|
556
559
|
: publicKey;
|
|
557
560
|
|
|
558
561
|
// API: verify(signature, message, publicKey)
|
|
559
|
-
const valid =
|
|
562
|
+
const valid = mlDsa65Verify(signatureBytes, payloadBytes, publicKeyBytes);
|
|
560
563
|
|
|
561
564
|
return { valid, reason: valid ? 'OK' : 'INVALID_SIGNATURE' };
|
|
562
565
|
} catch (err) {
|
|
@@ -591,7 +594,7 @@ export class GumbaProof {
|
|
|
591
594
|
const payload = JSON.stringify(attestation);
|
|
592
595
|
const payloadBytes = utf8ToBytes(payload);
|
|
593
596
|
// API: sign(message, secretKey)
|
|
594
|
-
const signature =
|
|
597
|
+
const signature = mlDsa65Sign(payloadBytes, grantorSecretKey);
|
|
595
598
|
|
|
596
599
|
return {
|
|
597
600
|
...attestation,
|
|
@@ -632,7 +635,7 @@ export class GumbaProof {
|
|
|
632
635
|
: grantorPublicKey;
|
|
633
636
|
|
|
634
637
|
// API: verify(signature, message, publicKey)
|
|
635
|
-
const valid =
|
|
638
|
+
const valid = mlDsa65Verify(signatureBytes, payloadBytes, publicKeyBytes);
|
|
636
639
|
|
|
637
640
|
return { valid, reason: valid ? 'OK' : 'INVALID_SIGNATURE' };
|
|
638
641
|
} catch (err) {
|
|
@@ -1236,9 +1239,12 @@ export class GumbaHub extends EventEmitter {
|
|
|
1236
1239
|
// Access sessions
|
|
1237
1240
|
this.sessions = new Map(); // sessionId -> { dokoId, bundleIds, expiresAt }
|
|
1238
1241
|
|
|
1239
|
-
// Public key registry (for testing/local lookups before
|
|
1242
|
+
// Public key registry (for testing/local lookups before KeyResolver integration)
|
|
1240
1243
|
this.publicKeys = new Map(); // dokoId -> publicKey
|
|
1241
1244
|
|
|
1245
|
+
// KeyResolver: unified key resolution (attached lazily)
|
|
1246
|
+
this.keyResolver = options.keyResolver || null;
|
|
1247
|
+
|
|
1242
1248
|
// Stats
|
|
1243
1249
|
this.stats = {
|
|
1244
1250
|
bundlesCreated: 0,
|
|
@@ -1379,8 +1385,27 @@ export class GumbaHub extends EventEmitter {
|
|
|
1379
1385
|
|
|
1380
1386
|
this.stats.messagesProcessed += result.count;
|
|
1381
1387
|
|
|
1382
|
-
//
|
|
1383
|
-
|
|
1388
|
+
// Deliver via ANNEX for E2E encryption to remote visitor
|
|
1389
|
+
if (this.annex && session.visitorNodeId) {
|
|
1390
|
+
try {
|
|
1391
|
+
await this.annex.send(session.visitorNodeId, {
|
|
1392
|
+
type: 'gumba:messages',
|
|
1393
|
+
sessionId,
|
|
1394
|
+
bundleId: session.bundleId,
|
|
1395
|
+
...result,
|
|
1396
|
+
});
|
|
1397
|
+
return { delivered: true, via: 'annex', count: result.count };
|
|
1398
|
+
} catch (err) {
|
|
1399
|
+
// HARD FAIL: No plaintext fallback. GUMBA content is encrypted for a reason.
|
|
1400
|
+
// Returning decrypted content in plaintext defeats the entire security model.
|
|
1401
|
+
log.error('ANNEX delivery failed — refusing plaintext return', {
|
|
1402
|
+
visitor: peerTag(session.visitorNodeId),
|
|
1403
|
+
error: err.message,
|
|
1404
|
+
});
|
|
1405
|
+
return { error: 'ENCRYPTION_REQUIRED', message: 'ANNEX session required for content delivery' };
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1384
1409
|
return result;
|
|
1385
1410
|
}
|
|
1386
1411
|
|
|
@@ -1475,11 +1500,15 @@ export class GumbaHub extends EventEmitter {
|
|
|
1475
1500
|
}
|
|
1476
1501
|
|
|
1477
1502
|
/**
|
|
1478
|
-
* Get DOKO public key
|
|
1479
|
-
*
|
|
1503
|
+
* Get DOKO public key — unified resolution cascade
|
|
1504
|
+
*
|
|
1505
|
+
* Resolution order:
|
|
1506
|
+
* 1. Local publicKeys map (test/pre-cached)
|
|
1507
|
+
* 2. Own identity
|
|
1508
|
+
* 3. KeyResolver (DOKO cache, peers, SHERPA, etc.)
|
|
1480
1509
|
*/
|
|
1481
1510
|
async _getDokoPublicKey(dokoId) {
|
|
1482
|
-
// Check local registry first
|
|
1511
|
+
// Check local registry first (backwards compat)
|
|
1483
1512
|
if (this.publicKeys.has(dokoId)) {
|
|
1484
1513
|
return this.publicKeys.get(dokoId);
|
|
1485
1514
|
}
|
|
@@ -1489,8 +1518,13 @@ export class GumbaHub extends EventEmitter {
|
|
|
1489
1518
|
return this.identity.identity.publicKey;
|
|
1490
1519
|
}
|
|
1491
1520
|
|
|
1492
|
-
//
|
|
1493
|
-
|
|
1521
|
+
// KeyResolver: unified key resolution
|
|
1522
|
+
if (this.keyResolver) {
|
|
1523
|
+
const key = this.keyResolver.resolve(dokoId);
|
|
1524
|
+
if (key) return key;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
log.warn('DOKO public key not found', { dokoId: dokoId.slice(0, 16) });
|
|
1494
1528
|
return null;
|
|
1495
1529
|
}
|
|
1496
1530
|
|