Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dist/
.vscode/
.idea/
src/background.bundle.js
popup/popup.bundle.js
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ stays inside the extension and never reaches the page.
git clone https://github.com/JavaScriptSolidServer/podkey.git
cd podkey
npm install
npm run build # bundles @noble deps into src/background.bundle.js
npm run build # bundles the background worker and passkey-enabled popup
```

Then load the `podkey` directory as an unpacked extension (steps 2–4 above).
Expand Down
12 changes: 12 additions & 0 deletions popup/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,18 @@ h2 {
transform: none;
}

.advanced-choice { margin-top: 12px; color: var(--text-muted); font-size: 12px; }
.advanced-choice summary { cursor: pointer; text-align: center; font-weight: 600; color: var(--accent); }
.advanced-choice p { margin-top: 7px; line-height: 1.45; }
.advanced-choice .advanced-audience { color: var(--text-muted); border-left: 2px solid var(--border-strong); padding-left: 9px; }
.text-action { width: 100%; margin-top: 10px; padding: 9px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); font: inherit; font-weight: 600; cursor: pointer; }
.divider { display: flex; align-items: center; gap: 8px; margin: 12px 0; color: var(--text-faint); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }
.divider::before, .divider::after { content: ''; height: 1px; flex: 1; background: var(--border); }
.settings-separated { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.btn-small { flex: none; padding: 6px 10px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--accent); font: inherit; font-size: 11px; font-weight: 700; cursor: pointer; }
.backup-ack { display: flex; align-items: center; gap: 8px; margin: 12px 0 4px; font-size: 12px; color: var(--text); cursor: pointer; }
.backup-ack input { accent-color: var(--accent); width: 15px; height: 15px; flex: none; cursor: pointer; }

/* ---- Identity ---- */
.identity-section .section-title {
margin-bottom: 10px;
Expand Down
67 changes: 66 additions & 1 deletion popup/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ <h1>Welcome to Podkey</h1>
</svg>
Import existing key
</button>
<details class="advanced-choice">
<summary>Advanced: passkey identity</summary>
<p class="advanced-audience">For managing agents or working under compliance rules: bind your key to a FIDO2 passkey — hardware-backed, phishing-resistant, and unlocked with biometrics or a security key.</p>
<button id="passkeyDerivedBtn" class="text-action" type="button">Create identity from a passkey</button>
<p>No passphrase. Your passkey recreates the same Nostr key. Export a backup in case the passkey is lost.</p>
</details>
</div>

<div class="info-box">
Expand Down Expand Up @@ -104,6 +110,8 @@ <h1>Unlock Podkey</h1>
</div>

<div class="section">
<button id="passkeyUnlockBtn" class="btn btn-primary" type="button" hidden>Unlock with passkey</button>
<div id="unlockDivider" class="divider" hidden><span>Recovery</span></div>
<label for="unlockPassphrase">Passphrase</label>
<input id="unlockPassphrase" type="password" class="input" placeholder="Your passphrase" autocomplete="current-password" />
<button id="unlockBtn" class="btn btn-primary" type="button">Unlock</button>
Expand Down Expand Up @@ -166,6 +174,56 @@ <h2>Import private key</h2>
</div>
</div>

<!-- Passkey Backup Screen (derived identity: the backup must be
acknowledged before anything is persisted) -->
<div id="passkeyBackupScreen" class="screen" style="display: none">
<div class="header">
<div class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="8" cy="15" r="4" />
<path d="M10.85 12.15 19 4" />
<path d="m18 5 2 2" />
<path d="m15 8 2 2" />
</svg>
</div>
<div>
<h2>Save your backup key</h2>
<p class="subtitle">Required before your identity is created</p>
</div>
</div>

<div class="section">
<div class="identity-field">
<div class="identity-label">Private key (nsec)</div>
<div class="identity-value" id="backupNsec">Loading…</div>
</div>
<button id="backupCopyBtn" class="btn-copy" type="button">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
<span class="btn-copy-label">Copy private key</span>
</button>
<div class="hint">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="9" x2="12" y2="13" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
<span>If your passkey is ever lost or unavailable, this backup is the <strong>only</strong> way to recover the identity. Store it somewhere safe and private — never share it.</span>
</div>
<label class="backup-ack">
<input type="checkbox" id="backupAckCheck" />
<span>I have stored this backup key safely</span>
</label>

<div class="actions">
<button id="backupCancelBtn" class="btn btn-secondary" type="button">Cancel</button>
<button id="backupFinishBtn" class="btn btn-primary" type="button" disabled>Create identity</button>
</div>
</div>
</div>

<!-- Main Screen (shown when key exists) -->
<div id="mainScreen" class="screen" style="display: none">
<div class="header">
Expand Down Expand Up @@ -223,6 +281,13 @@ <h1>Podkey</h1>
<span class="slider"></span>
</label>
</div>
<div class="settings-item settings-separated">
<div class="settings-text">
<div class="settings-label">Passkey unlock</div>
<div id="passkeySettingDesc" class="settings-desc">Use biometrics or a security key instead of typing your passphrase.</div>
</div>
<button id="enablePasskeyBtn" class="btn-small" type="button">Set up</button>
</div>
</div>

<div class="section">
Expand All @@ -241,6 +306,6 @@ <h1>Podkey</h1>
</div>
</div>

<script src="popup.js"></script>
<script src="popup.bundle.js"></script>
</body>
</html>
Loading
Loading