No protocol or message-protection changes. The version in the application header was a literal and had fallen behind, showing v5.6.0 while running 5.7.1. It now comes from package.json, and a test fails if a hard-coded one reappears or if meta.json, the README badge, the changelog and the docs disagree about the release. Documentation reorganised so that everything technical lives in doc/ with an index, and the root keeps only what belongs there by convention: README, SECURITY, CHANGELOG and LICENSE. - SECURITY.md rewritten. It listed a supported release line three major versions out of date and made claims the software does not make. It now states what is guaranteed, what is not, and how to report a problem. - SECURITY_DISCLAIMER.md and RESPONSIBLE_USE.md merged into doc/USE-POLICY.md, which says what the software cannot protect against rather than listing generic advice. - doc/SECURITY-ARCHITECTURE.md renamed to doc/ARCHITECTURE.md and rewritten around the session lifecycle, what verification gates, and how recovery works. - doc/CRYPTOGRAPHY.md rewritten: key schedule, the Double Ratchet, framing, and memory handling, with values taken from the source rather than restated. - doc/CONFIGURATION.md rewritten with the real file-type policy, ICE and TURN guidance, and the deployment caching rules that matter. - docs/webrtc-config.md moved to doc/CALLS.md and rewritten; the obsolete docs/webrtc-audit.md, a working document full of stale line numbers, removed along with the docs/ directory. - doc/CONTRIBUTING.md records what the recent regressions taught us about writing tests that can actually fail. - doc/README.md added as an index. Internal security review notes are excluded from the repository via .gitignore. Those describe attack paths against specific releases in enough detail to reproduce them, which is useful privately and harmful in public while users are still updating.
230 lines
21 KiB
HTML
230 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
|
|
<meta http-equiv="Content-Security-Policy"
|
|
content="default-src 'self';
|
|
script-src 'self';
|
|
style-src 'self' 'unsafe-inline';
|
|
font-src 'self' data:;
|
|
connect-src 'self' stun: stuns: turn: turns:;
|
|
img-src 'self' data:;
|
|
media-src 'self' blob:;
|
|
object-src 'none';
|
|
frame-src 'none';
|
|
worker-src 'self';
|
|
manifest-src 'self';
|
|
form-action 'self';
|
|
base-uri 'none';
|
|
upgrade-insecure-requests;">
|
|
<meta http-equiv="X-Content-Type-Options" content="nosniff">
|
|
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
|
|
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
|
|
|
|
<!-- PWA Manifest -->
|
|
<link rel="manifest" href="./manifest.json">
|
|
<link rel="icon" type="image/x-icon" href="./logo/favicon.ico?v=1785988424571">
|
|
|
|
<!-- PWA Meta Tags -->
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="format-detection" content="telephone=no">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<meta name="apple-mobile-web-app-title" content="SecureBit">
|
|
<meta name="application-name" content="SecureBit">
|
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
|
<meta name="msapplication-config" content="/browserconfig.xml">
|
|
|
|
<!-- iOS Splash Screens - Полная конфигурация с генератора -->
|
|
<!-- Landscape -->
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_17_Pro_Max__iPhone_16_Pro_Max_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 402px) and (device-height: 874px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_17_Pro__iPhone_17__iPhone_16_Pro_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16_Plus__iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 420px) and (device-height: 912px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_Air_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16__iPhone_15_Pro__iPhone_15__iPhone_14_Pro_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_16e__iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_11__iPhone_XR_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png">
|
|
|
|
<!-- iPad Landscape -->
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 1032px) and (device-height: 1376px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/13__iPad_Pro_M4_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/12.9__iPad_Pro_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1210px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/11__iPad_Pro_M4_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/11__iPad_Pro__10.5__iPad_Pro_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.9__iPad_Air_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.5__iPad_Air_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/10.2__iPad_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="./logo/splash/splash_screens/8.3__iPad_Mini_landscape.png">
|
|
|
|
<!-- Portrait -->
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 440px) and (device-height: 956px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_17_Pro_Max__iPhone_16_Pro_Max_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 402px) and (device-height: 874px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_17_Pro__iPhone_17__iPhone_16_Pro_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16_Plus__iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 420px) and (device-height: 912px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_Air_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16__iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_16e__iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_11__iPhone_XR_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png">
|
|
|
|
<!-- iPad Portrait -->
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 1032px) and (device-height: 1376px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/13__iPad_Pro_M4_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/12.9__iPad_Pro_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1210px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/11__iPad_Pro_M4_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/11__iPad_Pro__10.5__iPad_Pro_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.9__iPad_Air_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.5__iPad_Air_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/10.2__iPad_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png">
|
|
<link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="./logo/splash/splash_screens/8.3__iPad_Mini_portrait.png">
|
|
|
|
<!-- Apple Touch Icons -->
|
|
<link rel="apple-touch-icon" href="./logo/icon-180x180.png?v=1785988424571">
|
|
<link rel="apple-touch-icon" sizes="57x57" href="./logo/icon-57x57.png">
|
|
<link rel="apple-touch-icon" sizes="60x60" href="./logo/icon-60x60.png">
|
|
<link rel="apple-touch-icon" sizes="72x72" href="./logo/icon-72x72.png">
|
|
<link rel="apple-touch-icon" sizes="76x76" href="./logo/icon-76x76.png">
|
|
<link rel="apple-touch-icon" sizes="114x114" href="./logo/icon-114x114.png">
|
|
<link rel="apple-touch-icon" sizes="120x120" href="./logo/icon-120x120.png">
|
|
<link rel="apple-touch-icon" sizes="144x144" href="./logo/icon-144x144.png">
|
|
<link rel="apple-touch-icon" sizes="152x152" href="./logo/icon-152x152.png">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="./logo/icon-180x180.png?v=1785988424571">
|
|
|
|
<!-- Microsoft Tiles -->
|
|
<meta name="msapplication-TileColor" content="#ff6b35">
|
|
<meta name="msapplication-TileImage" content="./logo/icon-144x144.png">
|
|
<meta name="msapplication-config" content="./browserconfig.xml">
|
|
|
|
<!-- Theme colors -->
|
|
<meta name="theme-color" content="#ff6b35">
|
|
<meta name="msapplication-navbutton-color" content="#ff6b35">
|
|
|
|
<!-- Security Headers for PWA - CSP is already defined above -->
|
|
|
|
|
|
<!-- SEO -->
|
|
<title>SecureBit.chat — Private, Encrypted Messenger</title>
|
|
<meta name="description" content="SecureBit.chat is a free, end-to-end encrypted peer-to-peer messenger that runs entirely in your browser. No accounts, no servers storing your messages — ECDH P-384, AES-256-GCM and in-person SAS verification.">
|
|
<meta name="keywords" content="encrypted messenger, end-to-end encryption, peer-to-peer chat, P2P messenger, ECDH, DTLS, SAS verification, WebRTC, private chat, secure chat, no servers, no accounts, MITM protection, PFS">
|
|
<meta name="author" content="Volodymyr">
|
|
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1">
|
|
<link rel="canonical" href="https://securebit.chat/">
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:site_name" content="SecureBit.chat">
|
|
<meta property="og:title" content="SecureBit.chat — End-to-End Encrypted Peer-to-Peer Chat">
|
|
<meta property="og:description" content="A secure messenger that runs entirely in your browser. No accounts, no servers storing your messages. Bring your own STUN/TURN servers — we don't impose anything.">
|
|
<meta property="og:url" content="https://securebit.chat/">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:locale" content="en_US">
|
|
<meta property="og:image" content="https://securebit.chat/assets/social-card.png">
|
|
<meta property="og:image:secure_url" content="https://securebit.chat/assets/social-card.png">
|
|
<meta property="og:image:type" content="image/png">
|
|
<meta property="og:image:width" content="1200">
|
|
<meta property="og:image:height" content="630">
|
|
<meta property="og:image:alt" content="SecureBit.chat — end-to-end encrypted, peer-to-peer chat">
|
|
|
|
<!-- Twitter Card -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="SecureBit.chat — End-to-End Encrypted Peer-to-Peer Chat">
|
|
<meta name="twitter:description" content="A secure messenger that runs entirely in your browser. No accounts, no servers storing your messages.">
|
|
<meta name="twitter:image" content="https://securebit.chat/assets/social-card.png">
|
|
<meta name="twitter:image:alt" content="SecureBit.chat — end-to-end encrypted, peer-to-peer chat">
|
|
|
|
<!-- Structured data (schema.org) -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@graph": [
|
|
{
|
|
"@type": "WebSite",
|
|
"@id": "https://securebit.chat/#website",
|
|
"name": "SecureBit.chat",
|
|
"url": "https://securebit.chat/",
|
|
"description": "End-to-end encrypted, peer-to-peer chat that runs entirely in your browser.",
|
|
"inLanguage": "en"
|
|
},
|
|
{
|
|
"@type": "WebApplication",
|
|
"name": "SecureBit.chat",
|
|
"url": "https://securebit.chat/",
|
|
"applicationCategory": "CommunicationApplication",
|
|
"operatingSystem": "Any (modern web browser)",
|
|
"browserRequirements": "Requires WebRTC and the Web Crypto API",
|
|
"description": "A free, end-to-end encrypted peer-to-peer messenger. No accounts, no servers storing messages — ECDH P-384, AES-256-GCM and in-person SAS verification.",
|
|
"image": "https://securebit.chat/assets/social-card.png",
|
|
"license": "https://opensource.org/licenses/MIT",
|
|
"isAccessibleForFree": true,
|
|
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
|
"featureList": [
|
|
"End-to-end encryption (ECDH P-384, AES-256-GCM)",
|
|
"Peer-to-peer WebRTC connection with no message servers",
|
|
"In-person SAS verification against man-in-the-middle attacks",
|
|
"Custom STUN/TURN servers and relay-only privacy mode",
|
|
"Encrypted file transfer",
|
|
"Installable Progressive Web App with offline support"
|
|
],
|
|
"sameAs": [ "https://github.com/SecureBitChat/securebit-chat" ]
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
<!-- Render-blocking JS is deferred: classic deferred scripts and module scripts
|
|
both execute in document order after parsing, so React still runs before the
|
|
app modules below, but the parser / first paint is no longer blocked. -->
|
|
<script defer src="config/ice-servers.js?v=1785988424571"></script>
|
|
<script defer src="libs/react/react.production.min.js"></script>
|
|
<script defer src="libs/react-dom/react-dom.production.min.js"></script>
|
|
<!-- Prism syntax highlighting (vendored, offline). Tokenizes code as TEXT only —
|
|
it never executes the snippet. Loaded in manual mode (no auto-highlight).
|
|
Its CSS is loaded async via load-async-css.js (not paint-critical). -->
|
|
<script defer src="libs/prism/prism.js"></script>
|
|
<!-- Critical, paint-defining CSS stays render-blocking (avoids FOUC / layout shift). -->
|
|
<link rel="stylesheet" href="assets/tailwind.css?v=1785988424571">
|
|
<link rel="icon" type="image/x-icon" href="/logo/favicon.ico?v=1785988424571">
|
|
<!-- Preload only the fonts needed for first paint. fa-solid covers the bulk of UI
|
|
icons; fa-regular/fa-brands are loaded on demand by their CSS (rarely on the
|
|
first screen). Inter latin 400/700 cover body text and headings/buttons. -->
|
|
<link rel="preload" href="/assets/fontawesome/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin-400.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/assets/fonts/inter/files/inter-latin-700.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="stylesheet" href="/assets/fonts/inter/inter.css">
|
|
<link rel="stylesheet" href="src/styles/main.css?v=1785988424571">
|
|
<link rel="stylesheet" href="src/styles/animations.css?v=1785988424571">
|
|
<link rel="stylesheet" href="src/styles/components.css?v=1785988424571">
|
|
<!-- Non-critical CSS (FontAwesome ~102KB, Prism) loaded async — no longer blocks paint. -->
|
|
<script defer src="src/scripts/load-async-css.js?v=1785988424571"></script>
|
|
<noscript>
|
|
<link rel="stylesheet" href="/assets/fontawesome/css/all.min.css">
|
|
<link rel="stylesheet" href="libs/prism/prism.css">
|
|
</noscript>
|
|
<script defer src="src/scripts/fa-check.js?v=1785988424571"></script>
|
|
<!-- Update Manager - система принудительного обновления -->
|
|
<script defer src="src/utils/updateManager.js?v=1785988424571"></script>
|
|
<script type="module" src="src/components/UpdateChecker.jsx?v=1785988424571"></script>
|
|
<script type="module" src="dist/qr-local.js?v=1785988424571"></script>
|
|
<script type="module" src="src/components/QRScanner.js?v=1785988424571"></script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="dist/app-boot.js?v=1785988424571"></script>
|
|
<script type="module" src="dist/app.js?v=1785988424571"></script>
|
|
|
|
<script defer src="src/scripts/pwa-register.js?v=1785988424571"></script>
|
|
<script src="./src/pwa/install-prompt.js?v=1785988424571" type="module"></script>
|
|
<script src="./src/pwa/pwa-manager.js?v=1785988424571" type="module"></script>
|
|
<script defer src="./src/scripts/pwa-offline-test.js?v=1785988424571"></script>
|
|
<link rel="stylesheet" href="./src/styles/pwa.css?v=1785988424571">
|
|
</body>
|
|
</html> |