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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
15 changes: 10 additions & 5 deletions docs/PAGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand All @@ -19,9 +19,8 @@ This document maps `pages/` — what each page does and how a visitor moves betw
flowchart TD
INDEX["index.html<br/>Landing page"]

INDEX -->|"Link to upload a local database of unknown version"| ROUTER["router.html<br/>Detects KDBX format"]
INDEX -->|"Link to upload a local KDBX 3.1/4.x database"| APP["0x67.html<br/>Parses and decrypts"]
ROUTER -->|"Identified as KDBX 3.1/4.x,<br/>link to upload KDBX 3.1/4.x database"| APP
INDEX -->|"Link to open a local database"| LOCAL["local.html<br/>① read the file, detect KDBX format<br/>② embed the matching app in an iframe"]
LOCAL -->|"Identified as KDBX 3.1/4.x,<br/>embed 0x67.html in an iframe,<br/>hand off bytes in memory"| APP["0x67.html<br/>Parses and decrypts"]

INDEX -->|"Link to open from Google Drive"| DRIVE["cloud-google-drive.html<br/>① Google sign-in (GIS token popup)<br/>② pick a file, fetch bytes"]
INDEX -->|"Link to open from OneDrive"| ONEDRIVE["cloud-microsoft-onedrive.html<br/>① Microsoft sign-in<br/>② pick a file, fetch bytes"]
Expand All @@ -32,6 +31,12 @@ flowchart TD
DROPBOX -->|"Embed 0x67.html in an iframe,<br/>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.
Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/PIPELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion docs/RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion docs/REPRODUCING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:<hex> <output path>` 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:<hex> <output path>` 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.

Expand Down