Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
89168ef
fix(protocol): validate manifests received from peers against max chu…
lukyrys Jul 16, 2026
37ddf3f
fix(protocol): reject chunk data of unexpected length before hashing
lukyrys Jul 16, 2026
d8a50da
fix(shared): reject malformed manifest shapes with coded error
lukyrys Jul 16, 2026
a5fff0a
Merge remote-tracking branch 'origin/main' into fix/60-enforce-max-ch…
lukyrys Jul 22, 2026
680ebdd
fix(protocol): re-apply chunk length check after main merge
lukyrys Jul 22, 2026
3f797a6
test(protocol): cover wrong-length chunk rejection and peer ban
lukyrys Jul 22, 2026
d440031
fix(lish): human-readable sizes in chunk-size-too-large error detail
lukyrys Jul 22, 2026
3fa81f8
fix(lish): reject malformed files field and conflicting duplicate che…
lukyrys Jul 22, 2026
a881fcd
fix(protocol): map malformed peer manifests to retryable peer error
lukyrys Jul 22, 2026
6891205
fix(lish): validate file size and integer chunk size in manifests
lukyrys Jul 22, 2026
137d0eb
fix(lishnets): close peer stream when manifest request throws
lukyrys Jul 22, 2026
366bc0d
fix(downloader): surface terminal chunk-size error instead of retrying
lukyrys Jul 22, 2026
9880048
fix(frontend): retry next peer on oversized manifest in fallback loop
lukyrys Jul 22, 2026
835cb4d
fix(lish): reject fractional chunk size on create to match import
lukyrys Jul 22, 2026
5cad078
fix(downloader): drop over-limit manifest peers, fail only when all do
lukyrys Jul 22, 2026
71bafca
fix(downloader): live peer iteration, drop malformed peers, probe ter…
lukyrys Jul 22, 2026
d8b4e4f
fix(lish): reject non-array directories and links in manifests
lukyrys Jul 22, 2026
981f9ff
fix(downloader): require unanimous over-limit evidence before terminal
lukyrys Jul 22, 2026
c699249
fix(lish): validate checksum algo and string path/checksum fields
lukyrys Jul 22, 2026
94c355c
fix(protocol): reject peer manifest whose id mismatches the request
lukyrys Jul 22, 2026
2dc8e1b
Merge remote-tracking branch 'origin/main' into fix/60-enforce-max-ch…
lukyrys Jul 26, 2026
327648c
fix(downloader): fail an over-limit LISH on the first manifest, not a…
lukyrys Jul 27, 2026
a51ec07
fix(settings): keep the message size limit above the chunk size limit
lukyrys Jul 27, 2026
69b1d29
fix(downloader): keep the over-limit verdict when closing the probe s…
lukyrys Jul 27, 2026
8e3393e
fix(frontend): stop the peer fallback loop on an over-limit LISH
lukyrys Jul 27, 2026
286a8cf
Update language files
libersoft-org Jul 27, 2026
fb08a2b
Merge branch 'fix/60-enforce-max-chunk-size' of https://github.com/li…
libersoft-org Jul 27, 2026
86a8591
test(frontend): cover the over-limit search result across five peers
lukyrys Jul 27, 2026
c61f9c0
Merge branch 'fix/60-enforce-max-chunk-size' of https://github.com/li…
lukyrys Jul 27, 2026
da625b1
fix(downloader): keep a probe answer from failing a running download
lukyrys Jul 27, 2026
fe94540
refactor(shared): move the message size headroom out of the backend
lukyrys Jul 27, 2026
5e6d9c1
fix(shared): report raw bytes when a size limit rounds to the same text
lukyrys Jul 27, 2026
299b30f
fix(frontend): generalize the invalid manifest message
lukyrys Jul 27, 2026
8d3b554
test(protocol): drop a tautological assertion and a duplicate case
lukyrys Jul 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions backend/src/api/lishnets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,14 @@ export function initLISHnetsHandlers(networks: Networks, dataServer: DataServer,
try {
const { stream } = await network.dialProtocolByPeerId(p.peerID, LISH_PROTOCOL);
const client = new LISHClient(stream);
const lishs = await client.requestList();
await client.close();
return { lishs };
try {
const lishs = await client.requestList();
return { lishs };
} finally {
// Close in finally so a throwing request (peer error, validation) cannot leak
// the stream; swallow close errors so they never mask the request error.
await client.close().catch(() => {});
}
} catch (error: any) {
if (error instanceof CodedError) throw error;
console.error(`[Peers] Failed to get LISH list from ${p.peerID.slice(0, 12)}:`, error.message?.slice(0, 120) ?? error);
Expand All @@ -199,8 +204,13 @@ export function initLISHnetsHandlers(networks: Networks, dataServer: DataServer,
const { stream } = await network.dialProtocolByPeerId(p.peerID, LISH_PROTOCOL);
const client = new LISHClient(stream);
const onProgress = (received: number, total: number): void => broadcast('lishnets:manifestProgress', { lishID: p.lishID, peerID: p.peerID, received, total } satisfies ManifestProgressEvent);
const manifest = await client.requestManifest(p.lishID, onProgress);
await client.close();
let manifest;
try {
manifest = await client.requestManifest(p.lishID, onProgress);
} finally {
// Close in finally — a rejected manifest (validation, peer error) must not leak the stream.
await client.close().catch(() => {});
}
// Strip checksums from files and compute summary
const files = (manifest.files ?? []).map(f => {
const entry: { path: string; size: number; permissions?: string; modified?: string; created?: string } = { path: f.path, size: f.size };
Expand Down Expand Up @@ -238,8 +248,12 @@ export function initLISHnetsHandlers(networks: Networks, dataServer: DataServer,
const { stream } = await network.dialProtocolByPeerId(p.peerID, LISH_PROTOCOL);
const client = new LISHClient(stream);
const onProgress = (received: number, total: number): void => broadcast('lishnets:manifestProgress', { lishID: p.lishID, peerID: p.peerID, received, total } satisfies ManifestProgressEvent);
manifest = await client.requestManifest(p.lishID, onProgress);
await client.close();
try {
manifest = await client.requestManifest(p.lishID, onProgress);
} finally {
// Close in finally — a rejected manifest (validation, peer error) must not leak the stream.
await client.close().catch(() => {});
}
} catch (error: any) {
if (error instanceof CodedError) throw error;
console.error(`[Peers] Failed to add LISH ${p.lishID.slice(0, 8)} from ${p.peerID.slice(0, 12)}:`, error.message?.slice(0, 120) ?? error);
Expand Down
9 changes: 6 additions & 3 deletions backend/src/api/lishs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type DataServer } from '../lish/data-server.ts';
import { type ILISH, type IStoredLISH, type ILISHDetail, type ILISHListResult, type SuccessResponse, type CreateLISHResponse, type ImportLISHResponse, type LISHSortField, type SortOrder, type CompressionAlgorithm, DEFAULT_ALGO, sanitizeFilename, validateLISHStructure, CodedError, ErrorCodes, productName } from '@shared';
import { type ILISH, type IStoredLISH, type ILISHDetail, type ILISHListResult, type SuccessResponse, type CreateLISHResponse, type ImportLISHResponse, type LISHSortField, type SortOrder, type CompressionAlgorithm, DEFAULT_ALGO, sanitizeFilename, validateLISHStructure, formatSizeOverLimit, CodedError, ErrorCodes, productName } from '@shared';
import { createLISH, exportLISHToFile, importLISHFromFile, parseLISHFromJSON, runVerification } from '../lish/lish.ts';
import { DEFAULT_CHUNK_SIZE } from '@shared';
import { Utils } from '../utils.ts';
Expand Down Expand Up @@ -202,8 +202,11 @@ export function initLISHsHandlers(dataServer: DataServer, emit: EmitFn, broadcas
const chunkSize = p.chunkSize ?? DEFAULT_CHUNK_SIZE;
// Reject overly large chunkSize before the (potentially long) hashing pass.
const maxChunkSize: number = settings.get('network.maxChunkSize') ?? DEFAULT_MAX_CHUNK_SIZE;
if (typeof chunkSize !== 'number' || !Number.isFinite(chunkSize) || chunkSize <= 0) throw new CodedError(ErrorCodes.LISH_INVALID_CHUNK_SIZE, String(chunkSize));
if (chunkSize > maxChunkSize) throw new CodedError(ErrorCodes.LISH_CHUNK_SIZE_TOO_LARGE, `${chunkSize} > ${maxChunkSize}`);
// Match validateLISHStructure's contract (integer chunkSize) so a LISH this
// version creates is always one it can also import — a fractional size would
// pass creation/export but be rejected on import.
if (typeof chunkSize !== 'number' || !Number.isInteger(chunkSize) || chunkSize <= 0) throw new CodedError(ErrorCodes.LISH_INVALID_CHUNK_SIZE, String(chunkSize));
if (chunkSize > maxChunkSize) throw new CodedError(ErrorCodes.LISH_CHUNK_SIZE_TOO_LARGE, formatSizeOverLimit(chunkSize, maxChunkSize));
const threads = p.threads ?? 0; // 0 = all CPU threads
const minifyJSON = p.minifyJSON ?? false;
const compress = p.compress ?? false;
Expand Down
24 changes: 21 additions & 3 deletions backend/src/api/settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Settings, type SettingsData } from '../settings.ts';
import { applyNetworkLimits } from '../protocol/network-limits.ts';
import { Utils } from '../utils.ts';
import { type CompressionAlgorithm, type SuccessResponse, type ISettingsImportResult, CodedError, ErrorCodes } from '@shared';
import { type CompressionAlgorithm, type SuccessResponse, type ISettingsImportResult, CodedError, ErrorCodes, minMessageSizeFor } from '@shared';
const assert = Utils.assertParams;

const ALLOWED_ROOT_KEYS = new Set(['language', 'ui', 'audio', 'storage', 'network', 'system', 'export', 'input']);
Expand Down Expand Up @@ -54,6 +54,22 @@ export function initSettingsHandlers(settings: Settings): SettingsHandlers {
return settings.get(p.path);
}

/**
* Push network limits into the protocol layer, first repairing a stored message-size
* limit that could not carry one chunk. applyNetworkLimits() enforces the same floor at
* runtime; persisting it here as well keeps the settings screen from showing a value the
* protocol layer silently overrides.
*
* Used by the paths that write arbitrary values — the WS `settings.set` call and the
* settings import. Reset and factory reset write the defaults, which satisfy the floor
* by construction, so they call applyNetworkLimits() directly.
*/
async function persistAndApplyNetworkLimits(): Promise<void> {
const floor = minMessageSizeFor(settings.get().network.maxChunkSize);
if (settings.get().network.maxMessageSize < floor) await settings.set('network.maxMessageSize', floor);
applyNetworkLimits(settings.get().network);
}

async function set(p: { path: string; value: any }): Promise<boolean> {
assert(p, ['path', 'value']);
// Confine writes to known top-level settings groups. This rejects unknown
Expand All @@ -64,7 +80,7 @@ export function initSettingsHandlers(settings: Settings): SettingsHandlers {
await settings.set(p.path, p.value);
// Re-push all runtime limits on any network write (idempotent). Path-by-path
// matching used to miss whole-object writes such as path === 'network'.
if (rootKey === 'network') applyNetworkLimits(settings.get().network);
if (rootKey === 'network') await persistAndApplyNetworkLimits();
return true;
}

Expand Down Expand Up @@ -147,7 +163,9 @@ export function initSettingsHandlers(settings: Settings): SettingsHandlers {
skipped.push(entry.path);
}
}
applyNetworkLimits(settings.get().network);
// An imported file can carry a message limit below the chunk limit — repair it here
// too, not just on interactive writes.
await persistAndApplyNetworkLimits();
console.log(`✓ Settings restored: ${applied} applied, ${skipped.length} skipped`);
return { applied, skipped };
}
Expand Down
24 changes: 16 additions & 8 deletions backend/src/protocol/chunk-downloader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Mutex } from 'async-mutex';
import { type ChunkID, type IStoredLISH, type LISHid, ErrorCodes } from '@shared';
import { type ChunkID, type IStoredLISH, type LISHid, ErrorCodes, expectedChunkLength } from '@shared';
import { DataServer, type MissingChunk } from '../lish/data-server.ts';
import { LISHClient, type HaveChunks } from './lish-protocol.ts';
import { downloadLimiter } from './speed-limiter.ts';
Expand Down Expand Up @@ -299,18 +299,26 @@ export class ChunkDownloader {
}
continue;
}
// Verify chunk integrity before writing
// Reject bad chunk data before writing. The manifest fixes each chunk's exact byte
// length (a file's last chunk may be shorter than chunkSize); checking length before
// the O(n) hash stops a peer from forcing us to hash oversized payloads (bounded only
// by maxMessageSize) and rejects malformed data early.
const data = result.data;
const hasher = new Bun.CryptoHasher(lish.checksumAlgo as any);
hasher.update(data);
const actualHash = hasher.digest('hex');
if (actualHash !== chunk.chunkID) {
const expectedLen = expectedChunkLength(lish, chunk.fileIndex, chunk.chunkIndex);
let rejectReason: string | null = expectedLen >= 0 && data.length !== expectedLen ? `wrong length: expected ${expectedLen}B, got ${data.length}B` : null;
Comment on lines +307 to +308

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check lengths for every duplicate chunk slot

When a checksum appears in multiple slots, writeChunkToAllSlots later writes this same payload to every slot sharing the chunk ID, but this new check only compares the length for the queued slot. With a malformed manifest that lists the same checksum for a full chunk before a shorter last-chunk slot, a full-length payload passes here and is then written past the end of the shorter file while all matching slots are marked downloaded. Reject duplicate checksum groups with mismatched expected lengths, or validate the payload against every target before accepting it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3fa81f8 at the root cause — validateLISHStructure now rejects manifests where the same checksum is claimed by slots with different expected lengths (unsatisfiable by any single payload), so such a manifest never reaches the duplicate-slot write path. Tracking only short last chunks keeps the check O(#files) in memory.

if (!rejectReason) {
const hasher = new Bun.CryptoHasher(lish.checksumAlgo as any);
hasher.update(data);
const actualHash = hasher.digest('hex');
if (actualHash !== chunk.chunkID) rejectReason = `bad hash: expected ${chunk.chunkID.slice(0, 12)}, got ${actualHash.slice(0, 12)}`;
}
if (rejectReason) {
const count = (corruptCount.get(peerID) ?? 0) + 1;
corruptCount.set(peerID, count);
console.log(`[DL] Corrupt chunk from ${peerID.slice(0, 12)}: expected ${chunk.chunkID.slice(0, 12)}, got ${actualHash.slice(0, 12)} (${count}/${ChunkDownloader.MAX_CORRUPT_CHUNKS})`);
console.log(`[DL] Rejected chunk from ${peerID.slice(0, 12)} (${rejectReason}) (${count}/${ChunkDownloader.MAX_CORRUPT_CHUNKS})`);
await requeueChunk(chunk);
if (count >= ChunkDownloader.MAX_CORRUPT_CHUNKS) {
console.log(`[DL] Peer ${peerID.slice(0, 12)} banned: ${count} corrupt chunks`);
console.log(`[DL] Peer ${peerID.slice(0, 12)} banned: ${count} bad chunks`);
await peerManager.removeAwait(peerID, 'ban');
break;
}
Expand Down
39 changes: 38 additions & 1 deletion backend/src/protocol/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,32 @@ export class Downloader {
// Phase 1: fetch manifest from a peer if needed
if (this.state === 'awaiting-manifest') {
if (this.peerManager.size() === 0) return;
for (const [, client] of this.peerManager.entries()) {
// LIVE iteration on purpose (Map iterators tolerate deletes and visit entries
// added mid-loop): a peer joining via HAVE while we await another's manifest
// gets its turn in THIS pass — its doWork() trigger no-ops on the locked mutex.
for (const [peerID, client] of this.peerManager.entries()) {
let manifest: import('@shared').IStoredLISH | null = null;
try {
manifest = await client.requestManifest(this.lishID);
} catch (error: any) {
if (error instanceof CodedError && error.code === ErrorCodes.LISH_CHUNK_SIZE_TOO_LARGE) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep trying peers before failing oversized manifests

When an awaiting-manifest download has multiple peers and the first peer returns a forged or stale manifest whose chunkSize exceeds the local limit, this branch calls setError and returns before querying the remaining peers. requestManifest() derives this error from a single peer response, and the frontend fallback treats the same code as retryable for exactly this spoofing case, so one bad peer can prevent importing a valid manifest from another peer; exhaust the available peers first, then surface the limit error if they all fail the same way.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5cad078 — the awaiting-manifest loop now drops a peer whose manifest exceeds the limit (a forged/stale one from a single peer) and keeps trying the rest; another peer can still serve a valid manifest. The terminal LISH_CHUNK_SIZE_TOO_LARGE is surfaced only once every peer has been dropped this way (the LISH itself is over-limit), so one bad peer can no longer block a valid import, and a genuinely oversized LISH no longer stalls silently in awaiting-manifest. The probe path just drops such peers too and leaves the terminal decision to this loop.

// The peer delivered a well-formed manifest for the LISH we asked for and it
// declares a chunk size above our limit. Chunk size is a property of the LISH
// itself, so every honest peer serves the same value — asking the rest only
// makes the user watch each peer fail in turn before the same error appears.
// Surface it now and stop — unless the download was torn down while we
// awaited the manifest, in which case there is no state left to fail.
this.peerManager.remove(peerID, 'drop');
if (!this.destroyed) this.setError(error.code, error.detail);
return;
}
// A structurally malformed manifest (mapped to PEER_INVALID_REQUEST) is this
// peer's fault — keeping it would leave the download stuck asking the same
// bad peer forever while discovery skips it as "connected".
if (error instanceof CodedError && error.code === ErrorCodes.PEER_INVALID_REQUEST) {
this.peerManager.remove(peerID, 'drop');
continue;
}
console.warn(`[DL] Manifest request failed: ${error.message?.slice(0, 120) ?? error}`);
}
if (manifest && manifest.files && manifest.files.length > 0) {
Expand Down Expand Up @@ -571,6 +592,22 @@ export class Downloader {
try {
manifest = await probeClient.requestManifest(this.lishID);
} catch (error: any) {
// Any manifest error (unreachable, malformed) → drop this peer and let another
// serve it, except over-limit which is terminal for the whole LISH — but only
// while we are still looking for a manifest. Probing also runs mid-download
// purely to find more peers, and there requestManifest is just a "do you have
// this LISH?" test whose answer is discarded; failing the transfer on it would
// hand any peer on the topic a way to kill a healthy download.
if (this.needsManifest && error instanceof CodedError && error.code === ErrorCodes.LISH_CHUNK_SIZE_TOO_LARGE) {
// Same reasoning as the connected-peer loop: a delivered manifest that is over
// the limit answers the question for the whole LISH, so stop probing the rest.
// close() must not throw past this point — the outer catch would swallow the
// verdict, log it as an unreachable peer and let the probe loop carry on.
this.peerManager.remove(peerID, 'drop');
await probeClient.close().catch(() => {});
if (!this.destroyed) this.setError(error.code, error.detail);
return;
}
console.debug(`[DL] probe ${peerID.slice(0, 12)}: manifest error ${error.code ?? error.message?.slice(0, 60) ?? error}`);
this.peerManager.remove(peerID, 'drop');
}
Expand Down
36 changes: 34 additions & 2 deletions backend/src/protocol/lish-protocol.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { decode as lpDecode } from 'it-length-prefixed';
import { encode as lpEncode } from 'it-length-prefixed';
import { type Stream } from '@libp2p/interface';
import { type LISHid, type ChunkID, type ErrorCode, ErrorCodes, CodedError } from '@shared';
import { DEFAULT_MAX_MESSAGE_SIZE } from '../settings.ts';
import { type LISHid, type ChunkID, type ErrorCode, ErrorCodes, CodedError, validateLISHStructure } from '@shared';
import { DEFAULT_MAX_MESSAGE_SIZE, DEFAULT_MAX_CHUNK_SIZE } from '../settings.ts';
import { type DataServer } from '../lish/data-server.ts';
import { Uint8ArrayList } from 'uint8arraylist';
import { uploadLimiter } from './speed-limiter.ts';
Expand All @@ -26,6 +26,21 @@ export function getMaxMessageSize(): number {
return maxMessageSize;
}

/**
* Maximum chunk size accepted in a manifest received from a peer, in bytes.
* Mirrors the `network.maxChunkSize` bound enforced on locally created/imported LISHs so a
* malicious or malformed manifest can't push an oversized chunk size into the app. Read live
* on every manifest so settings changes take effect without a peer restart.
*/
let maxChunkSize: number = DEFAULT_MAX_CHUNK_SIZE;
export function setMaxChunkSize(size: number): void {
if (typeof size === 'number' && Number.isFinite(size) && size > 0) maxChunkSize = size;
}

export function getMaxChunkSize(): number {
return maxChunkSize;
}

export type LISHRequest = LISHGetChunkRequest | LISHGetLishRequest | LISHGetLishsRequest | LISHAnnounceHaveRequest | LISHSearchResultRequest;
export interface LISHGetChunkRequest {
type?: 'getChunk';
Expand Down Expand Up @@ -211,6 +226,23 @@ export class LISHClient {
const response = this.parseResponse<LISHGetLishResponse>(responseData, `getLish ${lishID}`);
if ('error' in response) throw new CodedError(response.error, lishID);
if (!('manifest' in response)) throw new CodedError(ErrorCodes.PEER_INVALID_REQUEST, `getLish ${lishID}: missing manifest`);
// The manifest must be for the LISH we asked for — a peer returning a different id
// would let a spoofing peer win the fallback loop and could import the wrong LISH
// under the requested id. Treat a mismatch as this peer's fault so fallback tries the next.
if (response.manifest?.id !== lishID) throw new CodedError(ErrorCodes.PEER_INVALID_REQUEST, `getLish ${lishID}: manifest id mismatch (${String(response.manifest?.id)})`);
// A manifest from the network is untrusted input — validate chunk-size bounds and
// manifest consistency before it can reach any caller (DB persist / import / probe).
try {
validateLISHStructure(response.manifest, maxChunkSize);
} catch (e) {
// A structurally malformed manifest is this peer's fault — surface it as a peer
// protocol error so fallback loops move on to the next peer. An over-limit
// chunkSize is a property of the LISH itself (every honest peer serves the same
// manifest), so it stays a terminal local error.
if (e instanceof CodedError && e.code !== ErrorCodes.LISH_CHUNK_SIZE_TOO_LARGE) throw new CodedError(ErrorCodes.PEER_INVALID_REQUEST, `getLish ${lishID}: ${e.message}`);
throw e;
}
// Emitted only after validation passes — a rejected manifest must not flash a full bar.
if (total > 0) safeEmit(total, total);
return response.manifest;
} finally {
Expand Down
10 changes: 8 additions & 2 deletions backend/src/protocol/network-limits.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type SettingsData } from '../settings.ts';
import { minMessageSizeFor } from '@shared';
import { Downloader } from './downloader.ts';
import { setMaxUploadSpeed, setMaxUploadPeersPerLISH, setMaxMessageSize } from './lish-protocol.ts';
import { setMaxUploadSpeed, setMaxUploadPeersPerLISH, setMaxMessageSize, setMaxChunkSize } from './lish-protocol.ts';
import { setMaxDownloadPeersPerLISH } from './peer-manager.ts';

/**
Expand All @@ -15,5 +16,10 @@ export function applyNetworkLimits(net: SettingsData['network']): void {
setMaxUploadSpeed(net.maxUploadSpeed);
setMaxDownloadPeersPerLISH(net.maxDownloadPeersPerLISH);
setMaxUploadPeersPerLISH(net.maxUploadPeersPerLISH);
setMaxMessageSize(net.maxMessageSize);
// A message limit at or below the chunk limit would reject every chunk on arrival, so
// the chunk limit wins and the message limit is lifted over it. Enforced here rather
// than at each writer: startup, WS API set/reset/import and factory reset all pass
// through this function, so no path can install an unusable pair.
setMaxMessageSize(Math.max(net.maxMessageSize, minMessageSizeFor(net.maxChunkSize)));
setMaxChunkSize(net.maxChunkSize);
}
Loading