diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5352bdd..bda4da8 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -137,6 +137,12 @@ jobs:
npm ci
npm run build:extension
+ - name: Generate extension checksums
+ run: |
+ cd dist
+ sha256sum koalasync-chrome.zip koalasync-firefox.zip > SHA256SUMS
+ cat SHA256SUMS
+
- name: Generate artifact attestation for extensions
uses: actions/attest@v4
with:
@@ -158,6 +164,7 @@ jobs:
files: |
dist/koalasync-chrome.zip
dist/koalasync-firefox.zip
+ dist/SHA256SUMS
name: Release ${{ github.ref_name }}
generate_release_notes: true
draft: false
diff --git a/README.md b/README.md
index 6c1b38f..6346d24 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ The easiest and safest way to install KoalaSync is directly through the official
-*(For manual offline installation: Download the latest `.zip` from the [Releases](https://github.com/Shik3i/KoalaSync/releases) page and load it as an "Unpacked Extension" in Developer Mode).*
+*(For manual offline installation: Download the latest `.zip` from the [Releases](https://github.com/Shik3i/KoalaSync/releases) page, extract it, and load the extracted browser package using your browser's developer or manual extension installation process.)*
**How to use:**
1. **Create a Room:** Click the Koala icon in your browser and hit `+ Create New Room`.
@@ -112,7 +112,14 @@ To verify your relay is reachable from outside, visit `https://your-domain.com`
#### Supply Chain Security (v2.2.2+)
-All official release artifacts (Docker images and extension binaries) are published with signed [artifact attestations](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations) to prove they were built from this repository's source code.
+Extension ZIPs attached to GitHub Releases and Docker images published to GitHub Container Registry receive signed [artifact attestations](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations). These attestations connect an artifact's exact digest to this repository, its release tag and commit, and the public GitHub Actions workflow that built it.
+
+Each new GitHub Release also includes `SHA256SUMS` for a quick checksum comparison:
+```bash
+sha256sum --check SHA256SUMS
+```
+
+Checksums detect a changed or incomplete download. Signed provenance additionally verifies where the artifact came from and which workflow built it. Neither check guarantees that the software contains no bugs or vulnerabilities. These attestations cover the GitHub release ZIPs and GHCR images; they do not by themselves prove that browser-store packages are byte-for-byte identical.
**Verify a Docker image:**
```bash
diff --git a/docs/devops.md b/docs/devops.md
index 4108f2c..2b249b2 100644
--- a/docs/devops.md
+++ b/docs/devops.md
@@ -24,7 +24,7 @@ When you push a Git tag matching `v*` (e.g., `v2.5.1`), the GitHub Actions relea
- `README.md` (updates badge and announcement banner)
- `website/sitemap.xml` (updates `lastmod` dates)
3. **Commits and pushes** these version updates back to the `main` branch automatically with the commit message `chore(release): update versions to vX.X.X [skip ci]`.
-4. **Builds the extension** for both Chrome and Firefox and publishes the zipped archives.
+4. **Builds the extension** for both Chrome and Firefox and publishes the zipped archives with a `SHA256SUMS` checksum file and signed provenance attestations.
5. **Builds the website** and uploads website artifacts.
6. **Builds and publishes** the Docker image for the relay server to the GitHub Container Registry (`ghcr.io`).
diff --git a/examples/Caddyfile.example b/examples/Caddyfile.example
index e74721e..ae3bed2 100644
--- a/examples/Caddyfile.example
+++ b/examples/Caddyfile.example
@@ -15,6 +15,10 @@
# root * /var/www/koalasync/website/www
# encode zstd gzip
#
+# # Keep one canonical URL for indexable support pages.
+# redir /help.html /help 308
+# redir /site-access-help.html /site-access-help 308
+#
# # Clean URLs support (resolves /join to join.html, etc.)
# try_files {path} {path}.html {path}/
# file_server
@@ -61,6 +65,10 @@ sync.koalastuff.net {
encode zstd gzip
root * /var/www/koalasync/website/www
+ # Keep one canonical URL for indexable support pages.
+ redir /help.html /help 308
+ redir /site-access-help.html /site-access-help 308
+
# Clean URLs: Resolves paths without .html in the URL
try_files {path} {path}.html {path}/
file_server
@@ -96,4 +104,3 @@ syncserver.koalastuff.net {
encode zstd gzip
reverse_proxy KoalaSync:3000
}
-
diff --git a/scripts/test-website-theme.mjs b/scripts/test-website-theme.mjs
index 89d3e4f..62aa059 100644
--- a/scripts/test-website-theme.mjs
+++ b/scripts/test-website-theme.mjs
@@ -102,6 +102,7 @@ if (!llmsText.includes(`Current website release: ${websiteVersion}`)) {
throw new Error(`llms.txt release must match website/version.json (${websiteVersion})`);
}
const requiredSupportSocialMetadata = [
+ '',
'name="twitter:card" content="summary_large_image"',
'name="twitter:title"',
'name="twitter:description"',
@@ -132,12 +133,12 @@ for (const metadata of requiredHelpMetadata) {
}
}
if (!llmsText.includes('[Help Center](https://sync.koalastuff.net/help)')
- || !llmsText.includes('[Website access guide](https://sync.koalastuff.net/site-access-help.html)')) {
+ || !llmsText.includes('[Website access guide](https://sync.koalastuff.net/site-access-help)')) {
throw new Error('llms.txt must expose the Help Center and website-access guide');
}
if (!websiteBuild.includes("['log', '-1', '--format=%cs', '--', ...sourceFiles]")
- || !websiteBuild.includes("lastmod(['website/help.html', 'website/styles/support.css'])")
- || !websiteBuild.includes("lastmod(['website/site-access-help.html'])")
+ || !websiteBuild.includes("lastmod(['website/help.html', 'website/styles/support.css', 'website/app.js'])")
+ || !websiteBuild.includes("lastmod(['website/site-access-help.html', 'website/app.js'])")
|| !websiteBuild.includes("['rev-parse', '--is-shallow-repository']")
|| !websiteBuild.includes("['diff', '--name-only', '--']")
|| !websiteBuild.includes("['diff', '--cached', '--name-only', '--']")
@@ -145,6 +146,9 @@ if (!websiteBuild.includes("['log', '-1', '--format=%cs', '--', ...sourceFiles]"
|| !websiteBuild.includes('sourceFiles.some(file => dirtySourceFiles.has(file))')) {
throw new Error('Sitemap lastmod values must come from the mapped source files in Git');
}
+if (websiteBuild.includes('') || websiteBuild.includes('')) {
+ throw new Error('Sitemap must not emit ignored changefreq or priority hints');
+}
const documentedRelayUrls = [...llmsText.matchAll(/`(wss:\/\/[^`\s]+)`/g)].map(([, value]) => new URL(value));
const hasCanonicalPublicRelay = documentedRelayUrls.some((url) => (
url.protocol === 'wss:' &&
@@ -179,6 +183,10 @@ for (const value of requiredSelfHostingValues) {
if ((selfHostingExamples.match(/localhost:3000/g) || []).length !== 2 || selfHostingExamples.includes('KoalaSync:3000')) {
throw new Error('Both Caddy examples must match the compose loopback port binding');
}
+if ((selfHostingExamples.match(/\/help\.html \/help 308/g) || []).length !== 2
+ || (selfHostingExamples.match(/\/site-access-help\.html \/site-access-help 308/g) || []).length !== 2) {
+ throw new Error('Both Caddy examples must redirect legacy support-page URLs to their clean canonicals');
+}
const landingStylesheet = landingStylesheets[0][0];
if (!/href="\{\{ASSET_PATH\}\}landing\.min\.css"/.test(landingStylesheet)) {
diff --git a/website/README.md b/website/README.md
index fe1bc69..fb84af1 100644
--- a/website/README.md
+++ b/website/README.md
@@ -73,6 +73,8 @@ Minimal static-site block:
```caddy
sync.koalastuff.net {
root * /var/www/koalasync/website/www
+ redir /help.html /help 308
+ redir /site-access-help.html /site-access-help 308
try_files {path} {path}.html {path}/
file_server
encode zstd gzip
diff --git a/website/app.js b/website/app.js
index 6fc1ddf..dd7bd6b 100644
--- a/website/app.js
+++ b/website/app.js
@@ -235,20 +235,55 @@ document.addEventListener('DOMContentLoaded', () => {
// Auto-update URL hash as user scrolls through sections
// (preserves position across language switches)
- if ('IntersectionObserver' in window) {
- const sectionObserver = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- if (entry.target.id === 'top') {
- history.replaceState(null, null, window.location.pathname + window.location.search);
- } else {
- history.replaceState(null, null, '#' + entry.target.id);
- }
- if (forestGreet) forestGreet();
- }
+ const trackedSections = [...document.querySelectorAll('section[id], header[id]')];
+ if (trackedSections.length > 0) {
+ let hashUpdateFrame = null;
+ let hashNavigationTimer = null;
+ let hashNavigationInProgress = false;
+ const updateSectionHash = () => {
+ hashUpdateFrame = null;
+ const readingLine = 112;
+ const activeSection = trackedSections.reduce((closest, section) => {
+ const distance = Math.abs(section.getBoundingClientRect().top - readingLine);
+ const closestDistance = Math.abs(closest.getBoundingClientRect().top - readingLine);
+ return distance < closestDistance ? section : closest;
});
- }, { threshold: 0.3 });
- document.querySelectorAll('section[id], header[id]').forEach(el => sectionObserver.observe(el));
+
+ const nextHash = activeSection.id === 'top' ? '' : '#' + activeSection.id;
+ if (window.location.hash === nextHash) return;
+ if (nextHash === '') {
+ history.replaceState(null, null, window.location.pathname + window.location.search);
+ } else {
+ history.replaceState(null, null, nextHash);
+ }
+ if (forestGreet) forestGreet();
+ };
+ const scheduleSectionHashUpdate = () => {
+ if (hashUpdateFrame !== null) return;
+ hashUpdateFrame = requestAnimationFrame(updateSectionHash);
+ };
+ const finishHashNavigation = () => {
+ hashNavigationTimer = null;
+ hashNavigationInProgress = false;
+ scheduleSectionHashUpdate();
+ };
+ const beginHashNavigation = () => {
+ hashNavigationInProgress = true;
+ if (hashNavigationTimer !== null) clearTimeout(hashNavigationTimer);
+ hashNavigationTimer = setTimeout(finishHashNavigation, 250);
+ };
+ const handleSectionScroll = () => {
+ if (!hashNavigationInProgress) {
+ scheduleSectionHashUpdate();
+ return;
+ }
+ if (hashNavigationTimer !== null) clearTimeout(hashNavigationTimer);
+ hashNavigationTimer = setTimeout(finishHashNavigation, 180);
+ };
+ window.addEventListener('hashchange', beginHashNavigation);
+ window.addEventListener('scroll', handleSectionScroll, { passive: true });
+ if (window.location.hash) beginHashNavigation();
+ else scheduleSectionHashUpdate();
}
// Navbar scroll effect (class-based so it follows the active theme)
diff --git a/website/build.cjs b/website/build.cjs
index 1f4ee77..8b89cc9 100644
--- a/website/build.cjs
+++ b/website/build.cjs
@@ -109,12 +109,22 @@ function stageFlagFontSubset(websiteDir, wwwDir) {
function copyDirSync(src, dest) {
fs.mkdirSync(dest, { recursive: true });
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
+ if (entry.name === '.DS_Store') continue;
const s = path.join(src, entry.name);
const d = path.join(dest, entry.name);
entry.isDirectory() ? copyDirSync(s, d) : fs.copyFileSync(s, d);
}
}
+function removeBuildMetadata(dir) {
+ if (!fs.existsSync(dir)) return;
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const entryPath = path.join(dir, entry.name);
+ if (entry.name === '.DS_Store') fs.rmSync(entryPath, { force: true });
+ else if (entry.isDirectory()) removeBuildMetadata(entryPath);
+ }
+}
+
function sha8(buf) { return crypto.createHash('sha256').update(buf).digest('hex').slice(0, 8); }
function sha384(buf) { return 'sha384-' + crypto.createHash('sha384').update(buf).digest('base64'); }
@@ -164,6 +174,7 @@ async function compile() {
const websiteDir = __dirname;
const wwwDir = path.join(websiteDir, 'www');
fs.mkdirSync(wwwDir, { recursive: true });
+ removeBuildMetadata(wwwDir);
// ββ 0. Auto-generate website logo sizes and sync favicons ββ
console.log('Generating responsive website logos...');
@@ -796,34 +807,24 @@ function generateSitemap(websiteDir, wwwDir) {
xml += `
https://sync.koalastuff.net/privacy${lastmod(['website/privacy.html'])}
- monthly
- 0.3
- https://sync.koalastuff.net/help${lastmod(['website/help.html', 'website/styles/support.css'])}
- weekly
- 0.8
+ https://sync.koalastuff.net/help${lastmod(['website/help.html', 'website/styles/support.css', 'website/app.js'])}
- https://sync.koalastuff.net/site-access-help.html${lastmod(['website/site-access-help.html'])}
- weekly
- 0.8
+ https://sync.koalastuff.net/site-access-help${lastmod(['website/site-access-help.html', 'website/app.js'])}
https://sync.koalastuff.net/de/datenschutz${lastmod(['website/datenschutz-de.html'])}
- monthly
- 0.3`;
- function addPage(relativePath, changefreq, priority, templateSource) {
+ function addPage(relativePath, templateSource) {
for (const lang of languages) {
const loc = `https://sync.koalastuff.net/${lang.prefix}${relativePath}`;
const sourceFiles = [templateSource, `website/locales/${lang.code}.json`];
xml += `
- ${loc}${lastmod(sourceFiles)}
- ${changefreq}
- ${priority}`;
+ ${loc}${lastmod(sourceFiles)}`;
for (const alt of languages) {
const altHref = `https://sync.koalastuff.net/${alt.prefix}${relativePath}`;
xml += `
@@ -836,8 +837,8 @@ function generateSitemap(websiteDir, wwwDir) {
}
}
- addPage('', 'weekly', '1.0', 'website/template.html');
- addPage('alternatives', 'weekly', '0.7', 'website/alternatives/index.html');
+ addPage('', 'website/template.html');
+ addPage('alternatives', 'website/alternatives/index.html');
const subpages = [
['alternatives/teleparty', 'website/alternatives/teleparty.html'],
@@ -848,7 +849,7 @@ function generateSitemap(websiteDir, wwwDir) {
['alternatives/twoseven', 'website/alternatives/twoseven.html']
];
for (const [sub, templateSource] of subpages) {
- addPage(sub, 'weekly', '0.7', templateSource);
+ addPage(sub, templateSource);
}
xml += `\n\n`;
diff --git a/website/help.html b/website/help.html
index 4dec076..e444e10 100644
--- a/website/help.html
+++ b/website/help.html
@@ -3,8 +3,8 @@
- KoalaSync Help | Setup, Video Detection & Troubleshooting
-
+ KoalaSync Help | Setup, Chat, Privacy & Troubleshooting
+
@@ -16,7 +16,7 @@
-
+
@@ -25,7 +25,7 @@
-
+
@@ -51,8 +51,8 @@
"@type": ["CollectionPage", "FAQPage"],
"@id": "https://sync.koalastuff.net/help#webpage",
"name": "KoalaSync Help",
- "headline": "KoalaSync Help: Setup, Video Detection and Troubleshooting",
- "description": "Help for granting browser access, finding missing video tabs, diagnosing video detection, fixing playback synchronization, and reporting reproducible bugs.",
+ "headline": "KoalaSync Help: Setup, Chat, Privacy and Troubleshooting",
+ "description": "Help for joining rooms, granting browser access, fixing encrypted room chat and playback synchronization, understanding data handling, and verifying official releases.",
"url": "https://sync.koalastuff.net/help",
"datePublished": "2026-07-28",
"dateModified": "2026-07-28",
@@ -68,6 +68,14 @@
"text": "KoalaSync works with video players across many websites. It uses access on the video tab you actively select to find and control the video element, synchronize playback, and display optional room chat. Open tab titles populate the tab selector; KoalaSync does not create a general browsing or watch history."
}
},
+ {
+ "@type": "Question",
+ "name": "Why does a KoalaSync invite link not join the room?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "Open the complete invite link in the same desktop browser profile where KoalaSync is installed. If the invite page was already open when the extension was installed or enabled, reload it. Confirm that the browser allows KoalaSync to run on sync.koalastuff.net, then ask the sender for a newly copied invite if the link is incomplete."
+ }
+ },
{
"@type": "Question",
"name": "Why does a video tab not appear in KoalaSync?",
@@ -84,6 +92,14 @@
"text": "Select the tab containing the visible player and start playback once. Check Video Debug Info in the Status tab, reload after permission changes, and temporarily disable extensions that modify the player. Some protected or non-HTML5 players may need site-specific support."
}
},
+ {
+ "@type": "Question",
+ "name": "Why does KoalaSync room chat not work for everyone?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "Room chat requires KoalaSync 3.0.0 or newer. The room creator receives a chat encryption secret when creating a new room, and every friend must join through that room's current invite link to receive the same secret. Manual room entry and old invite links can still join playback synchronization, but do not provide chat access."
+ }
+ },
{
"@type": "Question",
"name": "Why are connected KoalaSync participants not synchronized?",
@@ -91,6 +107,38 @@
"@type": "Answer",
"text": "Confirm that everyone uses the same KoalaSync version and selected the correct local video tab. Reload after website-access changes, test play, pause, and seeking separately, and collect a debug report from each affected participant."
}
+ },
+ {
+ "@type": "Question",
+ "name": "Does KoalaSync stream the video to other participants?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "No. KoalaSync does not stream, upload, proxy, share, or bypass access to video content. Everyone watches locally in their own browser and needs their own access to the website or streaming service. KoalaSync only synchronizes playback actions such as play, pause, and seeking."
+ }
+ },
+ {
+ "@type": "Question",
+ "name": "Which devices and browsers support KoalaSync?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "KoalaSync is a desktop browser extension for Chrome, Firefox, Edge, and compatible Chromium-based browsers on Windows, macOS, Linux, and ChromeOS. It does not currently provide a mobile app or Safari extension. Website compatibility also depends on the site's video player."
+ }
+ },
+ {
+ "@type": "Question",
+ "name": "What data does KoalaSync send from the browser?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "While you are in a room, KoalaSync sends the room, peer, and playback data needed for live synchronization. Display names and, depending on privacy settings, tab or media titles may be shared with room participants. Chat text is encrypted in the extension before transmission. KoalaSync does not create a persistent chat, browsing, synchronization, or watch history."
+ }
+ },
+ {
+ "@type": "Question",
+ "name": "How can official KoalaSync releases be verified?",
+ "acceptedAnswer": {
+ "@type": "Answer",
+ "text": "The extension, relay server, build scripts, release workflow, and Dockerfile are public under the MIT License. Extension ZIPs on GitHub Releases and Docker images on GitHub Container Registry receive signed provenance attestations that bind their exact digest to this repository, a release tag and commit, and the public GitHub Actions workflow."
+ }
}
]
},
@@ -147,50 +195,76 @@
Help center
-
How can we help?
-
Start with the symptom you see. These checks cover browser access, missing tabs, video detection, playback synchronization, and the information needed for a useful bug report.
+
KoalaSync Help & Troubleshooting
+
Start with the problem or question you have. These guides cover invites, browser access, video detection, encrypted chat, playback synchronization, supported devices, privacy, and release verification.
Browsers may say that KoalaSync can βread and change all your data on all websites.β The wording is broad because KoalaSync is designed to work with video players across many different websites instead of a short fixed list.
-
KoalaSync injects its synchronization script into the video tab you actively select. On that tab it needs to find and control the video element, synchronize play, pause, and seeking, and display the optional room chat overlay. Open tab titles are read to populate the tab selector. KoalaSync does not create a general browsing or watch history.
Open the invite in the same desktop browser profile where KoalaSync is installed. Extensions installed in another browser or profile cannot receive the join request.
+
If you installed, enabled, or updated KoalaSync after opening the invite, reload the invite page once so the extension can connect to it.
+
Confirm that KoalaSync is allowed to run on sync.koalastuff.net. If the browser blocks access, follow the site-access guide, then reload the invite.
+
Ask the sender to open KoalaSync and copy the invite link again. Use the complete link without removing anything after #; that part contains the room credentials and current chat secret.
+
Wait for the join page to report success. Joining the room does not select a video automatically, so open KoalaSync afterward and choose your local video tab under Sync.
+
+
+ i
+
+ The extension must be detected on the invite page.
+
If the page still offers an install button even though KoalaSync is installed, the usual causes are a different browser profile, blocked website access, or a page that was opened before the extension became available.
+
+
@@ -201,7 +275,7 @@
Close and reopen the KoalaSync popup, then check Select Video again.
Open KoalaSync Settings and turn off Hide Clutter Tabs.
Return to the tab selector. If the tab now appears, the site was filtered as clutter by mistake.
-
If the tab is still missing, verify website access using the site-access guide.
+
If the tab is still missing, verify website access using the site-access guide.
i
@@ -225,6 +299,26 @@
+
+ Room chat
+
Chat does not work, or one person cannot see it
+
Room chat was introduced in KoalaSync 3.0.0. Every participant who wants to send or read chat messages needs version 3.0.0 or newer. Older versions can still join a room and synchronize playback, but they do not support room chat.
+
+
Update KoalaSync in every browser. Open the extension and check the version in the top-right corner. Each participant needs version 3.0.0 or newer.
+
After updating, the room creator should leave the old room and click Create New Room. This creates a new chat encryption secret and includes it in the new invite link.
+
Send that newly copied invite link to your friends. Each friend must leave the old room and join through the link so their extension receives the same chat encryption secret.
+
Do not join by typing only the room ID and password, and do not reuse an old invite link beginning with #join:. Both can connect playback synchronization, but neither contains the chat secret.
+
Each participant must select their local video tab, then open Settings β Room Chat and turn on Enable Room Chat. The chat appears on the selected video page, not inside the extension popup.
+
+
+ i
+
+ Chat is live only.
+
There is no server-side message history. Someone who joins later sees only messages sent after they joined. Keep the invite link private: it contains the chat secret used to decrypt messages in that room.
+
+
+
+
Synchronization
Everyone is connected, but playback does not sync
@@ -237,6 +331,84 @@
+
+ Video access
+
Does KoalaSync send the video to my friends?
+
No. KoalaSync does not stream, upload, proxy, download, or redistribute video content. It only sends the live room and playback events needed to keep actions such as play, pause, and seeking synchronized.
+
+
Everyone watches the video locally in their own browser on the original website.
+
Each participant needs their own valid access to the website or streaming service.
+
KoalaSync does not bypass subscriptions, logins, DRM, regional restrictions, or platform rules.
+
Subtitles, audio tracks, fullscreen, and video quality remain local choices when the video player supports them.
+
+
+
+
+ Compatibility
+
Which devices and browsers are supported?
+
KoalaSync is currently a desktop browser extension. It supports Google Chrome, Mozilla Firefox, Microsoft Edge, Opera, Brave, Vivaldi, and other compatible Chromium-based desktop browsers on Windows, macOS, Linux, and ChromeOS.
+
+
There is currently no KoalaSync mobile app for phones or tablets.
+
There is currently no Safari extension.
+
Website compatibility depends on the browser and the site's video player. Websites can change their players, DRM behavior, or browser restrictions at any time.
+
Protected, customized, or non-HTML5 players may need site-specific support even in a supported browser.
+
+
+
+
+ Privacy & permissions
+
Why does the browser show such a broad warning?
+
Browsers may say that KoalaSync can βread and change all your data on all websites.β The wording is broad because KoalaSync is designed to work with video players across many different websites instead of a short fixed list.
+
KoalaSync injects its synchronization script into the video tab you actively select. On that tab it needs to find and control the video element, synchronize play, pause, and seeking, and display the optional room chat overlay. Open tab titles are read to populate the tab selector. KoalaSync does not create a general browsing or watch history.
KoalaSync only connects to a relay while you are actively in a room. During that session, the relay temporarily processes the data required to connect participants and synchronize playback.
+
+
Room and presence: room ID, peer ID, display name, connection state, and the information needed to join the room.
+
Playback synchronization: actions and state such as play, pause, seeking, and the current playback position.
+
Titles: tab or media titles may be shared with room participants depending on your title privacy settings. They can be limited or disabled.
+
Room chat: message text is encrypted in the extension before transmission. The relay receives ciphertext and delivery metadata, but not the chat secret or readable message text.
+
Stored locally: preferences, current room credentials, and the chat secret are stored in the browser. The chat secret is not sent to the relay. Decrypted chat entries remain only in the current page overlay and are not written to browser storage.
+
Operational logs: the relay may log technical connection and security information such as IP addresses, protocol versions, socket or peer identifiers, and shortened room identifiers. Retention depends on the server infrastructure and configuration.
+
+
KoalaSync does not create a persistent browsing, room, synchronization, chat, or watch history. Browser or operating-system notifications may retain notification text according to their own settings.
Trust should not depend on promises alone. The KoalaSync browser extension, relay server, build scripts, release workflow, and Dockerfile used for official GitHub releases are public under the MIT License.
+
Official extension ZIPs on GitHub Releases and Docker images on GitHub Container Registry are built by the public GitHub Actions release workflow. Signed SLSA provenance attestations connect each artifact's exact digest to this repository, its release tag and commit, and the workflow that built it.
+
You can install KoalaSync from an official browser store, download the Chrome or Firefox ZIP from GitHub Releases, build the extension yourself, or run your own relay server. Manual ZIP installation uses your browser's developer or manual extension installation process.
+ What an attestation proves, and what it does not.
+
An attestation proves the origin and build workflow of the GitHub release ZIP or GHCR image with that exact digest. It does not guarantee that software has no bugs or vulnerabilities. It also does not by itself prove that a package downloaded from a browser store is byte-for-byte identical to a GitHub release ZIP.