diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a95606e..0b2cf70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,7 +137,7 @@ jobs: - name: Record checksums run: | printf -- '### Distributable checksums\n' >> "$GITHUB_STEP_SUMMARY" - for page in index router 0x67 cloud-google-drive; do + for page in index local 0x67 cloud-google-drive; do checksum=$(node --experimental-strip-types tools/build/inliner/src/index.ts "pages/${page}/build.json" | awk '{print $1}') printf -- '- `%s.html`: `%s`\n' "${page}" "${checksum}" >> "$GITHUB_STEP_SUMMARY" done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9590067..f23f288 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,7 +103,7 @@ jobs: printf '### Distributable checksums\n| File | SHA-256 |\n|------|---------|\n' >> "$GITHUB_STEP_SUMMARY" - for page in index router 0x67 cloud-google-drive; do + for page in index local 0x67 cloud-google-drive; do checksum=$(node --experimental-strip-types tools/build/inliner/src/index.ts "pages/${page}/build.json" | awk '{print $1}') printf '| `%s.html` | `%s` |\n' "${page}" "${checksum}" >> "$GITHUB_STEP_SUMMARY" done @@ -137,7 +137,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | if gh attestation verify dist/0x67.html --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 && \ - gh attestation verify dist/router.html --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 && \ + gh attestation verify dist/local.html --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 && \ gh attestation verify dist/index.html --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 && \ gh attestation verify dist/cloud-google-drive.html --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 && \ gh attestation verify dist/CNAME --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then @@ -153,7 +153,7 @@ jobs: with: subject-path: | dist/0x67.html - dist/router.html + dist/local.html dist/index.html dist/cloud-google-drive.html dist/CNAME @@ -166,14 +166,14 @@ jobs: notes="### Distributable checksums"$'\n' notes+="| File | SHA-256 |"$'\n' notes+="|------|---------|"$'\n' - for file in dist/index.html dist/router.html dist/0x67.html dist/cloud-google-drive.html dist/CNAME; do + for file in dist/index.html dist/local.html dist/0x67.html dist/cloud-google-drive.html dist/CNAME; do notes+="| \`$(basename "${file}")\` | \`sha256:$(sha256sum "${file}" | awk '{print $1}')\` |"$'\n' done gh release create "$GITHUB_REF_NAME" \ --title "$GITHUB_REF_NAME" \ --notes "${notes}" \ dist/0x67.html \ - dist/router.html \ + dist/local.html \ dist/index.html \ dist/cloud-google-drive.html \ dist/CNAME diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 0735610..29d2ffb 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -33,7 +33,7 @@ All five are what CI runs on every push to `main` and pull request. packages/argon2/ Argon2d/Argon2id KDF — see its README.md and SPEC.md packages/chacha20/ ChaCha20/Salsa20 stream ciphers — see its README.md and SPEC.md packages/kdbx/ KDBX 3.1/4.x parser and serializer — see its README.md and SPEC.md -pages/ the browser app — index, router, and the KDBX 0x67 app itself +pages/ the browser app — index, local, and the KDBX 0x67 app itself tools/build/ the bundler and inliner that assemble each page into its own self-contained distributable, plus the ruleset and dependency policy CI checks docs/ this document, plus the pipeline, release, and reproducibility docs ``` diff --git a/docs/PAGES.md b/docs/PAGES.md index 09d7df6..8db1cb2 100644 --- a/docs/PAGES.md +++ b/docs/PAGES.md @@ -7,7 +7,7 @@ This document maps `pages/` — what each page does and how a visitor moves betw | Page | Availability | Description | |---|---|---| | `index.html` | GA | Landing page. The entry point; links to every other page. | -| `router.html` | GA | Detects a database's KDBX format and provides a link to the matching app page. | +| `local.html` | GA | Local-file connector: reads a dropped file, detects its KDBX format, and embeds the matching app page in an iframe. | | `0x67.html` | GA | The app — parses, decrypts, and edits KDBX 3.1 and 4.x databases. | | `cloud-google-drive.html` | GA | Connector for Google Drive. | | `cloud-microsoft-onedrive.html` | Future | Connector for OneDrive. | @@ -19,9 +19,8 @@ This document maps `pages/` — what each page does and how a visitor moves betw flowchart TD INDEX["index.html
Landing page"] - INDEX -->|"Link to upload a local database of unknown version"| ROUTER["router.html
Detects KDBX format"] - INDEX -->|"Link to upload a local KDBX 3.1/4.x database"| APP["0x67.html
Parses and decrypts"] - ROUTER -->|"Identified as KDBX 3.1/4.x,
link to upload KDBX 3.1/4.x database"| APP + INDEX -->|"Link to open a local database"| LOCAL["local.html
① read the file, detect KDBX format
② embed the matching app in an iframe"] + LOCAL -->|"Identified as KDBX 3.1/4.x,
embed 0x67.html in an iframe,
hand off bytes in memory"| APP["0x67.html
Parses and decrypts"] INDEX -->|"Link to open from Google Drive"| DRIVE["cloud-google-drive.html
① Google sign-in (GIS token popup)
② pick a file, fetch bytes"] INDEX -->|"Link to open from OneDrive"| ONEDRIVE["cloud-microsoft-onedrive.html
① Microsoft sign-in
② pick a file, fetch bytes"] @@ -32,6 +31,12 @@ flowchart TD DROPBOX -->|"Embed 0x67.html in an iframe,
hand off bytes in memory"| APP ``` +## How the local-file connector works + +`local.html` reads a dropped or chosen file's bytes once, right there in the tab — nothing is uploaded. It identifies the KDBX format from the file's first 8 bytes via the shared `packages/router` package (the same format-detection logic every chooser page uses, so which implementation reads a given format is decided in exactly one place), then embeds the matching implementation (currently only `0x67.html`) in an iframe and hands it the bytes it already read, over the same same-origin message protocol (`packages/embed-protocol`) the cloud connectors use. There is no second file picker: the file is never re-selected on the embedded app's own upload screen, because the app never shows one when opened this way. A file that's recognized but has no implementation yet (KeePass 1.x `.kdb`, a KDBX pre-release) is reported inline instead of embedding anything; a completely unrecognized file gets the same treatment. + +On save, since there is nowhere to write back to, the local connector downloads the updated bytes the same way `0x67.html` would if opened standalone — the only piece of this connector that's genuinely local-specific. + ## How the Google Drive connector works `cloud-google-drive.html` never parses or decrypts anything itself. It signs in to Google, lets the user pick a `.kdbx` file with the Google Picker, downloads its bytes, then embeds the real `0x67.html` app in an iframe and hands it those bytes. All the unlocking, browsing, and editing is the ordinary, unmodified app; the connector only fetches the file and writes it back. @@ -42,7 +47,7 @@ flowchart TD ## Local storage -Opening a database from local disk needs nothing but the file itself: no account, no sign-in, no network connection. `router.html` and `0x67.html` work completely offline, so a vault on a USB drive or a personal laptop opens the same way whether there's an internet connection or not. Nothing about the file goes anywhere — there's no vendor, no OAuth exchange, and no service to trust beyond the browser itself. Opening a local file needs no account of any kind and is open to every visitor. +Opening a database from local disk needs nothing but the file itself: no account, no sign-in, no network connection. `local.html` and `0x67.html` work completely offline, so a vault on a USB drive or a personal laptop opens the same way whether there's an internet connection or not. Nothing about the file goes anywhere — there's no vendor, no OAuth exchange, and no service to trust beyond the browser itself. Opening a local file needs no account of any kind and is open to every visitor. ## Cloud storage providers diff --git a/docs/PIPELINE.md b/docs/PIPELINE.md index 3e051f7..c1e7d85 100644 --- a/docs/PIPELINE.md +++ b/docs/PIPELINE.md @@ -99,7 +99,7 @@ flowchart TD TAG --> AREL["release.yml"] AREL --> CI3["Lint · type check · test"] CI3 --> VER2["Verify tag = package.json version"] - VER2 --> BUILD["Full build from source: compile\npackages and pages, bundle, inline;\ncopy CNAME\nOutputs: 0x67.html · router.html\nindex.html · cloud-google-drive.html\nCNAME\n(version footer: tag + commit date)"] + VER2 --> BUILD["Full build from source: compile\npackages and pages, bundle, inline;\ncopy CNAME\nOutputs: 0x67.html · local.html\nindex.html · cloud-google-drive.html\nCNAME\n(version footer: tag + commit date)"] BUILD --> ATTEST["Attest all five files\nactions/attest-build-provenance\nSigns to Sigstore transparency log"] ATTEST --> GHREL["Create GitHub release\nUpload all five files\nPublish checksums in release notes"] GHREL --> TOKEN["Generate short-lived App token\nscoped to the deploy repo"] @@ -119,7 +119,7 @@ flowchart TD TRIG --> CKO2["Checkout gh-pages branch"] CKO2 --> DL["Download all five release assets\nfrom public GitHub release URL\nNo token required"] DL --> V1["gh attestation verify 0x67.html"] - V1 --> V2["gh attestation verify router.html"] + V1 --> V2["gh attestation verify local.html"] V2 --> V3["gh attestation verify index.html"] V3 --> V3b["gh attestation verify cloud-google-drive.html"] V3b --> V4["gh attestation verify CNAME"] diff --git a/docs/RELEASES.md b/docs/RELEASES.md index ee80146..f5161b2 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -5,7 +5,7 @@ | File | Purpose | |------|---------| | `index.html` | Landing page | -| `router.html` | Identifies a database's KDBX format and links to the right app version | +| `local.html` | Identifies a local database's KDBX format and embeds the right app version in an iframe | | `0x67.html` | The app itself, for KDBX 3.1 and 4.x database support | | `cloud-google-drive.html` | Connector that opens and saves a database in the user's Google Drive, embedding `0x67.html` in an iframe | | `CNAME` | The custom domain GitHub Pages serves the deploy from | diff --git a/docs/REPRODUCING.md b/docs/REPRODUCING.md index 502187a..6006665 100644 --- a/docs/REPRODUCING.md +++ b/docs/REPRODUCING.md @@ -36,7 +36,7 @@ Any party can reproduce a versioned distributable and verify it against the publ sh -c "npm ci && npm run build" ``` - `npm run build` builds `argon2`, `chacha20`, and `kdbx`, then bundles and inlines each page. The inliner prints `sha256: ` to stdout once per distributable (`index.html`, `router.html`, `0x67.html`, `cloud-google-drive.html`), so each checksum is unambiguously tied to the file it belongs to. + `npm run build` builds `argon2`, `chacha20`, and `kdbx`, then bundles and inlines each page. The inliner prints `sha256: ` to stdout once per distributable (`index.html`, `local.html`, `0x67.html`, `cloud-google-drive.html`), so each checksum is unambiguously tied to the file it belongs to. 5. Compare each printed checksum against the corresponding value published with the release.