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
12 changes: 11 additions & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ on:
branches:
- main
paths:
- 'site/**'
- 'test-page/**'
- '.github/workflows/gh-pages.yml'
workflow_dispatch:

permissions:
contents: read
Expand All @@ -30,10 +32,18 @@ jobs:
- name: Setup Pages
uses: actions/configure-pages@v4

- name: Assemble site (docs landing + privacy + test page)
run: |
mkdir -p _site
cp -r site/. _site/
mkdir -p _site/test-page
cp -r test-page/. _site/test-page/
echo "Assembled _site:" && ls -R _site | head -40

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './test-page'
path: './_site'

- name: Deploy to GitHub Pages
id: deployment
Expand Down
47 changes: 40 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **NIP-44 (v2) encryption** — `window.nostr.nip44.encrypt(pubkey, plaintext)`
and `window.nostr.nip44.decrypt(pubkey, ciphertext)`, enabling NIP-17 / NIP-59
gift-wrapped direct messages. Crypto runs in the background service worker
(the private key never reaches the page), reusing the existing message-passing
path. Implemented with `@noble/ciphers` (chacha20) + `@noble/hashes`
(hkdf/hmac/sha256) + `@noble/secp256k1` (ECDH). Verified against the official
NIP-44 spec test vectors.
- **NIP-44 (v2) encryption.** `window.nostr.nip44.encrypt(pubkey, plaintext)`
and `nip44.decrypt(pubkey, ciphertext)` for NIP-17 / NIP-59 gift-wrapped
direct messages. The crypto runs in the background worker (`@noble/ciphers`
chacha20, `@noble/hashes` hkdf/hmac, ECDH via `@noble/secp256k1`) and is
checked against the official NIP-44 test vectors. The private key stays in
the worker.
- **Per-origin signing consent.** The first request from a site opens an
approval popup; closing it or a 60-second timeout denies. Approving grants
per-origin trust that you can revoke from the popup.
- **Encrypted-at-rest key vault.** The private key is persisted only as an
AES-256-GCM ciphertext in `chrome.storage.local`, wrapped by a scrypt-derived
key from the user's passphrase (`src/vault.js`). On unlock the decrypted key
is cached in `chrome.storage.session` for fast signing and cleared when the
browser closes; the raw key never touches disk. New `UNLOCK_VAULT`/`LOCK_VAULT`
messages, a popup unlock screen, and a clear "Podkey is locked" prompt replace
the previous "No keypair found" error after a restart.
- **NIP-98 Solid authentication.** Opt-in HTTP auth to Solid pods. Each token
carries a fresh 16-byte nonce and binds the request body hash and the final
redirect-aware URL, so one token authorises one request. Trusted Solid hosts
are matched exactly.
- **Schnorr self-verify.** Every signature is checked against the public key
before it is returned.
- **Extension icons** at 16, 48 and 128px, and a `script-src 'self'`
content-security policy across the popup and test page.
- **Continuous integration.** `.github/workflows/ci.yml` runs build, test and
lint on every pull request and push to `main`, and uploads a sideloadable
extension zip. The 141-case suite covers signing, NIP-44 vectors, NIP-98
token shape, the content-script message whitelist, the consent flow, and the
vault crypto (round-trip, wrong passphrase, tamper, salt/iv freshness).

### Changed

- A trusted origin signs any event kind without a prompt, so Podkey works as a
general NIP-07 signer. A normal client publishes kind 0, 10002 and 22242 on
every load; those no longer raise a prompt once you trust the site. Untrusted
origins still prompt, and approving one grants trust.
- The `window.nostr` provider exposes `getPublicKey`, `signEvent` and
`nip44.{encrypt,decrypt}`.
- The popup fits within the browser popup height, keeping the Export and GitHub
links in view.

## [0.0.7] - 2024-12-XX

Expand Down
75 changes: 75 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Podkey Privacy Policy

_Last updated: 2026-06-16_

Podkey is a browser extension that holds a Nostr/[did:nostr](https://nostrcg.github.io/did-nostr/)
key on your device and uses it to sign Nostr events ([NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md))
and to authenticate to [Solid](https://solidproject.org) pods over
[NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md). It is a
**local signer**: your key never leaves your device, and Podkey has no servers,
no accounts, and no analytics.

## What Podkey stores, and where

All data is stored **locally** in the browser's extension storage. Nothing is
sent to the Podkey developers or to any third party.

| Data | Where | Notes |
|------|-------|-------|
| **Private key** | `chrome.storage.local`, **encrypted** | Stored only as an AES-256-GCM ciphertext, wrapped by a key derived from your passphrase with scrypt. The plaintext key is never written to disk. |
| **Private key (unlocked)** | `chrome.storage.session`, in-memory | After you unlock with your passphrase, the decrypted key is held in memory for the browser session so signing is fast. It is cleared when the browser closes. |
| **Public key** | `chrome.storage.local` | Your `did:nostr` identity. Not secret. |
| **Trusted origins** | `chrome.storage.local` | The sites you have approved to sign without re-prompting, and an optional per-site auto-sign preference. You can revoke any of them from the popup. |

There is **no telemetry, no tracking, no advertising identifiers, and no
remote logging.** Podkey collects none of your browsing history or page content.

## How your key is used

- **Signing** (NIP-07 `signEvent`, `nip44.encrypt/decrypt`) and **Solid
authentication** (NIP-98) happen entirely inside the extension's background
service worker. The raw private key is never exposed to web pages — only the
resulting signature, ciphertext/plaintext, or `Authorization` header crosses
to the page.
- A site you have not approved triggers a consent prompt on its first request.
Closing the prompt, or a 60-second timeout, denies it.
- Podkey makes **no network requests of its own.** A NIP-98 `Authorization`
header is attached only to requests **you** initiate to a Solid/Nostr server
you have trusted; the data goes to that server, under your control, not to
Podkey.

## Permissions, and why they are needed

- **`storage`** — to keep the encrypted key, your public key, and your trusted
sites locally, as described above.
- **Host access (`<all_urls>`)** — Podkey is a NIP-07 signer, so it injects a
`window.nostr` provider into pages and, for sites you have trusted, attaches
NIP-98 auth to your own requests. This requires script access to the pages
where you use a Nostr/Solid app. Podkey **does not read, collect, or transmit
page content or browsing history**; host access is used solely to expose the
signer and to intercept auth for origins you have explicitly approved.

## Data sharing and retention

- Podkey **does not sell or share** any data. There is no data broker, no
third-party processor, and no off-device transfer of your key or activity.
- All data remains on your device until you delete it. **Removing the
extension, or using "Forget key" in the popup, erases the stored vault, public
key, and trusted sites.** Back up your private key before doing so — without a
backup it cannot be recovered.

## Your control

- Unlock / lock your key on demand from the popup.
- Review and revoke trusted sites at any time.
- Export your private key (after unlocking) to back it up.
- Forget the key entirely to start over.

## Contact

Podkey is open source under AGPL-3.0. Questions, issues, or security reports:
<https://github.com/JavaScriptSolidServer/podkey/issues>.

This policy may be updated as the extension evolves; the "Last updated" date
above reflects the current version. Material changes will be noted in the
project [CHANGELOG](CHANGELOG.md).
Loading
Loading