From 5ad496608f0813e6faae08b94496f48e81993f5c Mon Sep 17 00:00:00 2001 From: lashman Date: Sun, 22 Mar 2026 09:06:44 +0200 Subject: [PATCH] security settings explainer, spinner for velocity threshold --- .../web/src/pages/admin/AdminSecurity.tsx | 76 ++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/packages/web/src/pages/admin/AdminSecurity.tsx b/packages/web/src/pages/admin/AdminSecurity.tsx index a6e2c16..9584068 100644 --- a/packages/web/src/pages/admin/AdminSecurity.tsx +++ b/packages/web/src/pages/admin/AdminSecurity.tsx @@ -6,7 +6,8 @@ import { useDocumentTitle } from '../../hooks/useDocumentTitle' import { useConfirm } from '../../hooks/useConfirm' import { useToast } from '../../hooks/useToast' import Dropdown from '../../components/Dropdown' -import { IconPlus, IconTrash, IconCheck, IconX, IconAlertTriangle, IconShieldCheck } from '@tabler/icons-react' +import NumberInput from '../../components/NumberInput' +import { IconPlus, IconTrash, IconCheck, IconX, IconAlertTriangle, IconShieldCheck, IconChevronDown } from '@tabler/icons-react' // types @@ -437,6 +438,7 @@ function AlertsTab() { > Board security settings +
{boards.map((board) => ( @@ -448,10 +450,57 @@ function AlertsTab() { ) } +function SecurityExplainer() { + const storageKey = 'echoboard-security-explainer-open' + const [open, setOpen] = useState(() => { + const stored = localStorage.getItem(storageKey) + return stored === null ? true : stored === 'true' + }) + + const toggle = () => { + const next = !open + setOpen(next) + localStorage.setItem(storageKey, String(next)) + } + + return ( +
+ + {open && ( +
+
+ Sensitivity - controls how aggressively the system flags suspicious activity on this board. Normal is fine for most boards. High increases the proof-of-work difficulty for new or flagged users and tightens anomaly detection thresholds, making it harder for bots and brigaders to operate. Use High for boards where votes directly influence product decisions. +
+
+ Velocity threshold - the maximum number of votes per hour that the system considers normal for this board. When votes exceed this number, it triggers an alert. Set to 0 to let the system calculate it automatically from the board's historical average. Override it if you know your board has unusual traffic patterns. +
+
+ Quarantine - locks down the board during an active attack. When enabled, all new posts require admin approval before appearing publicly, vote counts are hidden, and new identities (created less than an hour ago) can browse but not vote, post, or comment. Existing users are unaffected. Turn it off once the situation is resolved. +
+
+ Vote verification - adds extra proof-of-work difficulty for new identities (less than an hour old) voting on this board. This makes it more expensive for brigaders to create throwaway accounts and vote in bulk, without adding friction for established users. +
+
+ )} +
+ ) +} + function BoardSecurityCard({ board }: { board: BoardSummary }) { const toast = useToast() const [sensitivity, setSensitivity] = useState(board.sensitivityLevel || 'normal') - const [threshold, setThreshold] = useState(board.velocityThreshold ?? '') + const [threshold, setThreshold] = useState(board.velocityThreshold ?? 0) const [quarantined, setQuarantined] = useState(board.quarantined || false) const [voteVerification, setVoteVerification] = useState(board.requireVoteVerification || false) const [saving, setSaving] = useState(false) @@ -461,7 +510,7 @@ function BoardSecurityCard({ board }: { board: BoardSummary }) { try { const res = await api.put<{ sensitivityLevel: string; velocityThreshold: number | null; quarantined: boolean; requireVoteVerification: boolean }>(`/admin/boards/${board.id}/security`, data) setSensitivity(res.sensitivityLevel) - setThreshold(res.velocityThreshold ?? '') + setThreshold(res.velocityThreshold ?? 0) setQuarantined(res.quarantined) setVoteVerification(res.requireVoteVerification) toast.success('Board security updated') @@ -507,23 +556,16 @@ function BoardSecurityCard({ board }: { board: BoardSummary }) { aria-label="Sensitivity level" />
-
-