diff --git a/docs/privacy-policy.md b/docs/privacy-policy.md
index 0b9a1b2..be04119 100644
--- a/docs/privacy-policy.md
+++ b/docs/privacy-policy.md
@@ -1,6 +1,6 @@
# PassKey Privacy Policy
-**Last updated:** 2026-03-15
+**Last updated:** 2026-05-10
---
@@ -39,6 +39,7 @@ The extension requests only the minimum permissions required:
|------------|---------|
| `nativeMessaging` | Communicate with PassKey Desktop via Native Messaging |
| `activeTab` | Read the current tab's URL to match credentials |
+| `tabs` | Inject autofill into the active tab and keep the popup's tab reference current |
---
diff --git a/docs/privacy/index.html b/docs/privacy/index.html
new file mode 100644
index 0000000..b87d85c
--- /dev/null
+++ b/docs/privacy/index.html
@@ -0,0 +1,343 @@
+
+
+
+
+
+ Privacy Policy
+ Your data stays on your device. Always.
+ Last updated: May 10, 2026 · Applies to PassKey Desktop and Browser Extension
+
+
+
TL;DR
+
PassKey never sends your data anywhere. No cloud, no servers, no analytics, no telemetry.
+ Everything stays encrypted on your computer.
+
+
+
+
+ 1. Overview
+ PassKey is a local-first password manager for Windows. The desktop application and its
+ browser extension store and manage your credentials exclusively on your device.
+ No account is required to use PassKey. No data is ever transmitted to any remote server
+ — by design, there is no remote server to transmit data to.
+ This policy describes what information PassKey reads or processes while running on
+ your computer and how that information is used.
+
+
+
+
+ 2. Data Storage
+
+ - All vault data (passwords, credit cards, identities, secure notes) is stored in an
+ encrypted SQLite database on your local disk.
+ - Default location:
%LOCALAPPDATA%\PassKey\vault.db
+ - Encryption: AES-256-GCM, keys derived from your master password via
+ Argon2id (or PBKDF2-SHA256 for vaults created on older versions).
+ - Your master password is never persisted — it is held in memory only for the duration
+ it is needed to derive the decryption key, then zeroed.
+
+
+
+
+
+ 3. Network Activity
+ PassKey makes zero outbound network connections. The only communication
+ that occurs is between the browser extension and the PassKey Desktop application on your
+ own computer, via the browser's Native Messaging API over a local Named Pipe:
+
+ - This communication never leaves your machine.
+ - The channel is protected with ephemeral ECDH P-256 + AES-256-GCM
+ session keys negotiated at runtime.
+
+ There is no analytics, no telemetry, no crash reporting, no update checking,
+ and no advertising — not now, not ever.
+
+
+
+
+ 4. Browser Extension Permissions
+ The PassKey browser extension (available for Chrome, Edge, and Firefox) requests the
+ minimum permissions necessary to operate. Below is a complete list of what each
+ permission is used for:
+
+
+
+
+ | Permission |
+ Why it is needed |
+ What it accesses |
+
+
+
+
+ nativeMessaging |
+ Communicate with PassKey Desktop via the browser's Native Messaging API |
+ Local IPC channel to PassKey Desktop — no internet access |
+
+
+ activeTab |
+ Read the URL of the current tab to find matching credentials |
+ URL only — no page content, no cookies, no form data |
+
+
+ tabs |
+ Inject autofill into the active tab and keep the popup's tab reference current |
+ Active tab ID and URL — no browsing history |
+
+
+
+
+ The extension reads the URL of the tab you are currently
+ viewing solely to identify which saved credentials match the site. This URL is passed
+ to the local PassKey Desktop app for matching and is never stored by the extension or
+ sent anywhere else.
+
+
+
+
+ 5. Data Sharing
+ PassKey does not share any data with third parties. There are no third-party SDKs,
+ advertising networks, or analytics providers embedded in PassKey. There is no data
+ to share because no data leaves your device.
+
+
+
+
+ 6. Backups
+ Encrypted backups (.pkbak files) are stored locally at a location you
+ choose. Backups are independently encrypted with AES-256-GCM using an Argon2id-derived
+ key from a password you provide at backup time. PassKey does not offer or access any
+ cloud backup service.
+
+
+
+
+ 7. Open Source & Auditability
+ PassKey is open-source software licensed under the
+ GNU GPL v3.
+ The complete source code is publicly available. You can audit every line of code that
+ handles your data at
+ github.com/pexatar/PassKey.
+
+
+
+
+ 8. Changes to This Policy
+ If this policy is updated, the new version will be published at this URL with an
+ updated date at the top. Because PassKey collects no personal data, changes will
+ typically only reflect new features or clarifications to existing practices.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/chrome/background.js b/extensions/chrome/background.js
index 8d6f28c..070985d 100644
--- a/extensions/chrome/background.js
+++ b/extensions/chrome/background.js
@@ -221,7 +221,7 @@ async function handleMessage(msg, sender) {
async function handleGetStatus() {
try {
const req = buildRequest('get-status');
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
return resp;
} catch (err) {
return { success: false, error: err.message || 'desktop-not-running' };
@@ -238,7 +238,7 @@ async function handleGetStatus() {
async function handleGetCredentials(url) {
try {
const req = buildRequest('get-credentials', { url });
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
return resp;
} catch (err) {
return { success: false, error: err.message || 'desktop-not-running' };
@@ -253,7 +253,7 @@ async function handleGetCredentials(url) {
async function handleGetAllCredentials() {
try {
const req = buildRequest('get-all-credentials');
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
return resp;
} catch (err) {
return { success: false, error: err.message || 'desktop-not-running' };
@@ -272,7 +272,7 @@ async function handleCopyCredential(credentialId) {
try {
await ensureSession();
const req = buildRequest('get-credential-password', { id: credentialId });
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
if (!resp.success) return resp;
let password;
@@ -297,7 +297,7 @@ async function handleCopyCredential(credentialId) {
async function handleUnlockVault(masterPassword) {
try {
const req = buildRequest('unlock-vault', { masterPassword });
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
return resp;
} catch (err) {
return { success: false, error: err.message || 'unlock-failed' };
@@ -312,7 +312,7 @@ async function handleUnlockVault(masterPassword) {
async function handleShowWindow() {
try {
const req = buildRequest('show-window');
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
return resp;
} catch (err) {
return { success: false, error: err.message || 'show-window-failed' };
@@ -371,7 +371,7 @@ async function handleFillCredential(credentialId, username, tabId) {
// Request encrypted password
const req = buildRequest('get-credential-password', { id: credentialId });
- const resp = await sendNativeMessage(req);
+ const resp = parseResponse(await sendNativeMessage(req));
if (!resp.success) {
return resp;
diff --git a/extensions/chrome/lib/i18n.js b/extensions/chrome/lib/i18n.js
index c62d2e9..cf492b6 100644
--- a/extensions/chrome/lib/i18n.js
+++ b/extensions/chrome/lib/i18n.js
@@ -103,7 +103,7 @@ const STRINGS = {
unlockBtn: 'Déverrouiller',
wrongPassword: 'Mot de passe incorrect. Réessayez.',
unlocking: 'Déverrouillage...',
- emptyTitle: 'Aucune identifiant',
+ emptyTitle: 'Aucun identifiant',
emptySub: 'Aucun identifiant enregistré.',
loadingText: 'Connexion...',
tabThisSite: 'Ce site',
@@ -215,6 +215,6 @@ const STRINGS = {
};
// Resolve the two-character primary language subtag from the browser locale.
-// Falls back to Italian ('it') for unsupported locales.
-const lang = (navigator.language || 'it').slice(0, 2).toLowerCase();
-window.t = STRINGS[lang] || STRINGS['it'];
+// Falls back to English ('en') for unsupported locales.
+const lang = (navigator.language || 'en').slice(0, 2).toLowerCase();
+window.t = STRINGS[lang] || STRINGS['en'];
diff --git a/extensions/chrome/manifest.json b/extensions/chrome/manifest.json
index d9357b2..5a524d3 100644
--- a/extensions/chrome/manifest.json
+++ b/extensions/chrome/manifest.json
@@ -2,18 +2,24 @@
"manifest_version": 3,
"name": "PassKey",
"version": "1.0.0",
+ "author": "Giuseppe Imperato",
+ "homepage_url": "https://github.com/pexatar/PassKey",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4pcZ6gFcMwryE8/OgBRW0lFn7a8kad6/J84GostBKfqf1at5RVaGW31arX54L+usiJ4EUizkwaTvUKKtoWaxyUYJ3nOxPNfrRysoJQwjP50IBQdOnR7VXqcKzO+L8xvfcdsK4CHsyTX7bU1Q/xoeawR9Or5yVKSPhNy86A63Qa2z2Y2QIyCB713jrElycb2sOsK/szEkIpG5teJCg/NA1nZ1VQXMu8j+kROaKNL0atpZAUoef2EGLU6uZK46es+HyvGXDZk6ZiQoey3FPhz5iMAd2ATS7Ml+9+TuTSE+tvtmF2swWI8SvWBshxa5a7y/UGE2Qqnr/XWY1eNLhP4MuQIDAQAB",
- "description": "PassKey Password Manager - Autofill Extension",
+ "description": "Local password manager integration for PassKey desktop app. Autofill credentials, credit cards and identities stored securely on your PC — no cloud, no subscription.",
"permissions": [
"nativeMessaging",
- "activeTab"
+ "activeTab",
+ "tabs"
],
+ "content_security_policy": {
+ "extension_pages": "script-src 'self'; object-src 'none';"
+ },
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
- "matches": ["