@kummahiih/circle-enroll
v0.1.3
Published
Browser enroll assets (PBKDF2 + WebAuthn PRF) for page-scoped password hashes used by private-circle and hello-circle
Maintainers
Readme
@kummahiih/circle-enroll
Browser enroll assets for page-scoped PBKDF2 / WebAuthn-PRF password hashes.
Used by @kummahiih/private-circle and hello-circle.
Assets
| File | Role |
|------|------|
| assets/enroll.html | Enrollment UI (password or passkey) |
| assets/enroll.css | Styles (same-origin file; no inline CSS) |
| assets/enroll-core.js | PBKDF2 path + shared helpers |
| assets/enroll-prf.js | WebAuthn PRF path |
| assets/enroll-json.md | JSON schema v1 + same-origin PRF notes |
JSON schema v1 matches private-circle enrollment format.
Lock pageId (gated sites)
Public copy (npx circle-enroll copy / circle-enroll.vercel.app) stays editable: the pageId field is shown and can be prefilled with ?page=.
When private-circle encrypt copies enroll into dist/, it stamps the root element:
<html lang="fi" data-page-id="my-site" data-lock-page-id="1">Then applyPageIdLock hides the pageId input and the #pageId-fixed chip. The hidden #pageId input stays filled so the PRF script does not change. Encrypt default is lock-on; --no-lock-page-id leaves the field editable.
Install
npm install @kummahiih/circle-enrollCLI
npx circle-enroll copy --out <dir>Writes enroll.html, enroll.css, enroll-core.js, and enroll-prf.js into the target directory.
WebAuthn PRF support
See docs/WEBAUTHN_PRF_SUPPORT.md and the full matrix in private-circle.
Rough minimums: Chrome/Edge 116+, Safari 18+ (platform only), Firefox 135+. Enroll and gate must share origin for PRF.
Operator runbook (production)
- Same-origin enroll for PRF — Host
enroll.htmlon the same origin as the gated page. WebAuthn credentials are RP-ID bound; a public enroll host on a different domain works for PBKDF2 only. - Prefer WebAuthn-PRF for high-value circles (smaller offline attack surface). Keep a password backup enrollment if recovery after passkey loss is required.
- Hashes hygiene — Never commit real user hashes to public git; never publish enroll JSON with
dist/. Demo sites may ship labeled public demo hashes only. - Rotate on leak — If hashes may have leaked together with published masks, rotate the build key
Kand re-enroll everyone.
Full detail: docs/THREAT_MODEL.md and @kummahiih/private-circle security notes.
Content Security Policy (strict)
Same-origin static assets only — not third-party CDNs, not inline code, not nonces.
| Directive | Value | Assets |
|-----------|--------|--------|
| script-src | 'self' | enroll-core.js, enroll-prf.js |
| style-src | 'self' | enroll.css |
| connect-src | 'none' | No network on enroll |
| 'unsafe-inline' / 'unsafe-eval' | not used | — |
| Nonces / 'strict-dynamic' | not used | Need dynamic HTML; break offline static hosting |
Default meta CSP on enroll.html:
default-src 'none'; base-uri 'none'; form-action 'none';
script-src 'self'; style-src 'self'; connect-src 'none';
img-src 'none'; font-src 'none'; object-src 'none'; frame-ancestors 'none'Prefer the HTTP CSP header (e.g. Vercel headers) as the source of truth; keep the meta tag aligned or omit it.
Consumption
private-circle
@kummahiih/private-circle resolves enroll assets from this package when encrypting or running init. Gate assets (gate.js / gate.css) stay in private-circle. Encrypt stamps data-page-id from --page-id unless --no-lock-page-id.
hello-circle / your site
npx circle-enroll copy --out distServe enroll on the same origin as the gated page for WebAuthn-PRF unlock.
License
Apache-2.0
