Gate joinInvite and navigateDeepLink with shared paste validation (P410-30).

Reject malformed invites and deep links at the platform layer using
validatePastedNavigationInput before mesh join or navigation side effects.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-06-02 14:47:05 -04:00
co-authored by Cursor
parent 5c387d416d
commit 51c4a8daa1
2 changed files with 184 additions and 15 deletions
+2
View File
@@ -12,6 +12,8 @@ Application facade: one `PearcordPlatform` class that wires identity, database,
**Phase 410 hardening (v0.8.643):** `view()` now supports explicit detail levels (`minimal`/`light`/`full`/`diagnostic`) through `opts.detail`, keeps legacy `opts.light` compatibility, and surfaces the resolved mode as `viewDetailLevel` in payload diagnostics.
**Phase 410 hardening (v0.8.643):** `guildSyncHealthRail` now carries bounded-retry metadata (`retryAttempt`, `retryDelayMs`, `retryMaxAttempts`, `nextRetryAt`, `retryCountdownMs`) so UI can show live “mesh bounded mode” retry countdown/tooltip state during background join retries.
**Phase 410 hardening (v0.8.643):** Guild mesh peer retry scheduling now uses `_computeGuildMeshPeerRetryDelayMs` (last-peer-contact staleness + RTT bounds) instead of fixed attempt-index spacing; health rows track `lastPeerSeenAt` on peer join.
**Phase 410 hardening (P410-20):** Repeated slow `guild.open` spans arm `guildOpenFallbackThrottle` (rolling window + cooldown): shorter bounded mesh cap, deferred emoji/archive/member-recovery fallbacks, reduced member bootstrap sync wait; view + `guildSyncHealthRail.openFallbackThrottled`. Smoke: `npm run test:platform-guild-open-fallback-throttle`.
**Phase 410 hardening (v0.8.643):** `_requestGuildMeshJoin` coalesces duplicate guild mesh joins during `guild.open`, peer retry, and startup/switch races; stale `loadGen` joins are skipped; `pearcord-guild#joinMesh` deduplicates in-flight `swarm.join`. Smoke: `npm run test:platform-guild-mesh-join-coalesce`.
**Phase 410 hardening (v0.8.643):** `_evictStaleGuildMeshPeers` drops idle hyperswarm sockets (env `PEARCORD_GUILD_MESH_PEER_STALE_MS`, periodic sweep) and records `peerRetryAttempt`, `lastPeerSeenAt`, and eviction totals on `guildSyncHealthRail`. Smoke: `npm run test:platform-guild-mesh-stale-peer-evict`.
**Phase 410 hardening (v0.8.643):** `_auditChannelViewConsistency` runs during `view()` to detect pin/reaction/message mismatches, repair stale pin/reaction list caches, and expose `channelConsistencyAudit` for diagnostics. Smoke: `npm run test:platform-channel-consistency-audit`.
+182 -15
View File
@@ -231,6 +231,8 @@ const {
contentHasChannelMention,
extractFirstUrl,
parsePearcordDeepLink,
validatePastedNavigationInput,
pasteValidationToast,
formatPearcordDeepLink,
formatGuildDeepLink: buildGuildDeepLink,
formatInviteDeepLink: buildInviteDeepLink,
@@ -508,6 +510,109 @@ class PearcordPlatform extends EventEmitter {
this._lastPartitionHealByGuild = new Map()
this._partitionHealInProgressByGuild = new Set()
this._partitionHealCooldownUntilByGuild = new Map()
this._guildOpenDurationSamples = []
this._guildOpenFallbackThrottledUntil = 0
}
_getGuildOpenSlowThresholdMs () {
return Math.max(
500,
Number(process.env.PEARCORD_GUILD_OPEN_SLOW_MS) || 3000
)
}
_getGuildOpenThrottleWindowMs () {
return Math.max(
10000,
Number(process.env.PEARCORD_GUILD_OPEN_THROTTLE_WINDOW_MS) || 120000
)
}
_getGuildOpenThrottleTriggerCount () {
return Math.max(
1,
Number(process.env.PEARCORD_GUILD_OPEN_THROTTLE_COUNT) || 2
)
}
_getGuildOpenThrottleCooldownMs () {
return Math.max(
5000,
Number(process.env.PEARCORD_GUILD_OPEN_THROTTLE_COOLDOWN_MS) || 60000
)
}
_guildOpenSlowCountInWindow () {
const now = Date.now()
const windowMs = this._getGuildOpenThrottleWindowMs()
const thresholdMs = this._getGuildOpenSlowThresholdMs()
return this._guildOpenDurationSamples.filter(
(s) => now - s.at <= windowMs && s.durationMs >= thresholdMs
).length
}
_isGuildOpenFallbackThrottled () {
return Date.now() < (this._guildOpenFallbackThrottledUntil || 0)
}
_guildOpenFallbackThrottleRemainingMs () {
return Math.max(0, (this._guildOpenFallbackThrottledUntil || 0) - Date.now())
}
getGuildOpenFallbackThrottle () {
return {
active: this._isGuildOpenFallbackThrottled(),
until: this._guildOpenFallbackThrottledUntil || 0,
remainingMs: this._guildOpenFallbackThrottleRemainingMs(),
slowCount: this._guildOpenSlowCountInWindow(),
thresholdMs: this._getGuildOpenSlowThresholdMs(),
windowMs: this._getGuildOpenThrottleWindowMs(),
triggerCount: this._getGuildOpenThrottleTriggerCount(),
cooldownMs: this._getGuildOpenThrottleCooldownMs()
}
}
_recordGuildOpenDuration (guildId, durationMs) {
const at = Date.now()
const windowMs = this._getGuildOpenThrottleWindowMs()
this._guildOpenDurationSamples.push({
guildId: guildId || null,
durationMs: Math.max(0, Number(durationMs) || 0),
at
})
const cutoff = at - windowMs
while (
this._guildOpenDurationSamples.length &&
this._guildOpenDurationSamples[0].at < cutoff
) {
this._guildOpenDurationSamples.shift()
}
const maxSamples = Math.max(20, this._getGuildOpenThrottleTriggerCount() * 4)
if (this._guildOpenDurationSamples.length > maxSamples) {
this._guildOpenDurationSamples.splice(
0,
this._guildOpenDurationSamples.length - maxSamples
)
}
const thresholdMs = this._getGuildOpenSlowThresholdMs()
if (durationMs >= thresholdMs) {
const slowCount = this._guildOpenSlowCountInWindow()
if (slowCount >= this._getGuildOpenThrottleTriggerCount()) {
const until = at + this._getGuildOpenThrottleCooldownMs()
if (until > (this._guildOpenFallbackThrottledUntil || 0)) {
this._guildOpenFallbackThrottledUntil = until
this.log.info('guild.open fallback throttle armed', {
spanKind: 'guild.open.fallback-throttle-arm',
guildId: guildId || null,
durationMs,
slowCount,
thresholdMs,
until,
cooldownMs: this._getGuildOpenThrottleCooldownMs()
})
}
}
}
}
_isGuildMeshBounded (guildId) {
@@ -666,7 +771,11 @@ class PearcordPlatform extends EventEmitter {
Number(this._guildMeshStalePeerEvictedTotalByGuild.get(id)) || 0,
lastStalePeerEvictAt: Number(h?.lastStalePeerEvictAt) || 0,
retrySpacingSource:
this._guildMeshPeerRetryMetaByGuild.get(id)?.spacingSource || null
this._guildMeshPeerRetryMetaByGuild.get(id)?.spacingSource || null,
openFallbackThrottled:
this._isGuildOpenFallbackThrottled() &&
(id === this.guild?.guild?.id || id === this._guildLoadingGuildId),
openFallbackThrottleRemainingMs: this._guildOpenFallbackThrottleRemainingMs()
}
}
return out
@@ -2025,6 +2134,7 @@ class PearcordPlatform extends EventEmitter {
const force = opts.force === true
const loadGen = opts.loadGen ?? null
const bounded = opts.bounded !== false
const fallbackThrottled = opts.fallbackThrottled === true
const g = this.guild
if (!g?.guild) {
return { peers: 0, bounded: false, skipped: true, reason: 'no-guild' }
@@ -2065,19 +2175,32 @@ class PearcordPlatform extends EventEmitter {
force,
loadGen,
bounded,
guildId
guildId,
fallbackThrottled
})
this._guildMeshJoinCoalesce = { guildId, topic, guild: g, loadGen, promise }
return promise
}
async _executeGuildMeshJoin ({ source, force, loadGen, bounded, guildId }) {
async _executeGuildMeshJoin ({
source,
force,
loadGen,
bounded,
guildId,
fallbackThrottled = false
}) {
const meshFlushMs = Number(
process.env.PEARCORD_GUILD_SWARM_FLUSH_MS ??
process.env.PEARCORD_SWARM_FLUSH_MS ??
2500
)
const joinMeshCapMs = meshFlushMs + 5000
const joinMeshCapMs = fallbackThrottled
? Math.max(
500,
Number(process.env.PEARCORD_GUILD_OPEN_THROTTLE_MESH_CAP_MS) || 1500
)
: meshFlushMs + 5000
try {
const g = this.guild
if (!g?.guild || g.guild.id !== guildId) {
@@ -13417,8 +13540,21 @@ class PearcordPlatform extends EventEmitter {
}
async _openGuild (guildRecord, loadGen = null) {
const span = this.log.time('guild.open', { guildId: guildRecord.id })
const openStartedAt = Date.now()
const fallbackThrottled = this._isGuildOpenFallbackThrottled()
const span = this.log.time('guild.open', {
guildId: guildRecord.id,
fallbackThrottled
})
try {
if (fallbackThrottled) {
this.log.info('guild.open fallback-throttled', {
spanKind: 'guild.open.fallback-throttle',
guildId: guildRecord.id,
remainingMs: this._guildOpenFallbackThrottleRemainingMs(),
slowCount: this._guildOpenSlowCountInWindow()
})
}
const leaveSpan = this.log.time('guild.open.leaveMeshes')
await this._leaveMeshes()
if (!this._isGuildLoadCurrent(loadGen)) {
@@ -13447,7 +13583,8 @@ class PearcordPlatform extends EventEmitter {
await this._requestGuildMeshJoin({
source: 'guild-open',
loadGen,
bounded: true
bounded: true,
fallbackThrottled
})
} catch (err) {
this.log.error('guild.mesh error', {
@@ -13501,30 +13638,46 @@ class PearcordPlatform extends EventEmitter {
})
}, 0)
this._startAuditExportScheduleTimer()
void this._maybeArchiveHealthRefreshOnFocus().catch(() => {})
this._restartArchiveHealthRefreshTimer()
if (this.activeChannelId && !this._guildOpenNoViewableChannels) {
if (!fallbackThrottled) {
void this._maybeArchiveHealthRefreshOnFocus().catch(() => {})
this._restartArchiveHealthRefreshTimer()
}
if (
this.activeChannelId &&
!this._guildOpenNoViewableChannels &&
!fallbackThrottled
) {
void this._prefetchEmojiAttachmentsAfterOpen().catch((err) => {
this.log.debug('emoji batch prefetch after guild open failed', { err })
})
}
if (guildRecord.ownerId !== user.id) {
const hostPk = await this._lookupGuildHostPublicKey(guildRecord)
const syncWaitMs = fallbackThrottled
? Math.max(
500,
Number(process.env.PEARCORD_GUILD_OPEN_THROTTLE_SYNC_WAIT_MS) || 2000
)
: 18000
void this._bootstrapGuildSessionAfterJoin({
invite: hostPk
? { creatorId: guildRecord.ownerId, creatorPublicKey: hostPk }
: null,
hostPublicKey: hostPk,
skipMeshJoin: true,
syncWaitMs: 18000
syncWaitMs
}).catch((err) => {
this.log.warn('guild.open member sync bootstrap failed', {
guildId: guildRecord.id,
err: err?.message || String(err)
})
})
this._scheduleGuildMemberSyncRecovery(guildRecord.id, hostPk)
if (!fallbackThrottled) {
this._scheduleGuildMemberSyncRecovery(guildRecord.id, hostPk)
}
}
const durationMs = Date.now() - openStartedAt
this._recordGuildOpenDuration(guildRecord.id, durationMs)
span.end({
guildId: guildRecord.id,
channels: channels.length,
@@ -13533,9 +13686,14 @@ class PearcordPlatform extends EventEmitter {
activeChannelId: this.activeChannelId,
guildCount: (this.guilds || []).length,
noViewableChannels: !!this._guildOpenNoViewableChannels,
durationMs,
fallbackThrottled,
openFallbackThrottleActive: this._isGuildOpenFallbackThrottled(),
spanKind: 'guild.open',
logLevel:
this._guildOpenNoViewableChannels || this._isGuildMeshBounded(guildRecord.id)
this._guildOpenNoViewableChannels ||
this._isGuildMeshBounded(guildRecord.id) ||
fallbackThrottled
? 'info'
: undefined
})
@@ -13947,7 +14105,11 @@ class PearcordPlatform extends EventEmitter {
}
async joinInvite (code) {
const raw = String(code || '').trim()
const validated = validatePastedNavigationInput(code)
if (!validated.ok) {
throw new Error(pasteValidationToast(validated))
}
const raw = validated.normalized
if (raw.toLowerCase().startsWith('pearcord://')) {
return this.navigateDeepLink(raw)
}
@@ -14325,8 +14487,12 @@ class PearcordPlatform extends EventEmitter {
}
async navigateDeepLink (url) {
const parsed = parsePearcordDeepLink(url)
if (!parsed) throw new Error('invalid pearcord deep link')
const validated = validatePastedNavigationInput(url)
if (!validated.ok) {
throw new Error(pasteValidationToast(validated))
}
const parsed = validated.parsed || parsePearcordDeepLink(validated.normalized)
if (!parsed) throw new Error(pasteValidationToast({ ok: false, code: 'unrecognized_deep_link' }))
if (parsed.kind === 'explore') {
const preserveBaseline =
@@ -21866,6 +22032,7 @@ class PearcordPlatform extends EventEmitter {
sessionError: this._sessionError,
guildLoading: this._guildLoading,
guildLoadingGuildId: this._guildLoadingGuildId,
guildOpenFallbackThrottle: this.getGuildOpenFallbackThrottle(),
user,
mode: this.mode,
guilds: this.guilds,