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
8 changes: 3 additions & 5 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ jobs:
npm run validate:full:ci

- name: Prepare site
run: |
mkdir -p _site
cp index.html _site/index.html
cp CHANGELOG.md _site/CHANGELOG.md
cp .release-please-manifest.json _site/.release-please-manifest.json
env:
UMAMI_WEBSITE_ID: ${{ vars.UMAMI_WEBSITE_ID }}
run: npm run prepare:pages

- name: Upload artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ The product is distributed as one standalone `index.html` artifact and is offlin

## Privacy and trust boundary

hSQLite Editor has no application backend and does not intentionally upload databases, queries, history, or settings. Files and browser storage remain on the device unless the user explicitly exports or shares them.
hSQLite Editor has no application backend and does not intentionally upload databases, queries, history, or settings. The downloaded standalone HTML, `file://` use, and local development contain no usage tracker. Only the official GitHub Pages deployment conditionally loads Umami for minimized pageview statistics, including an editor-version tag; it excludes URL query parameters and fragments and honors DNT and GPC.

The browser, browser extensions, operating system, downloaded artifact, and files selected by the user remain outside the project's trust boundary. Browser storage is not an encrypted secrets vault. The complete localStorage, IndexedDB, recent-file metadata, clearing, and permission-revocation inventory is in [docs/privacy.md](docs/privacy.md). See [SECURITY.md](SECURITY.md) and [security_posture.md](security_posture.md).
The browser, browser extensions, operating system, downloaded artifact, files selected by the user, and hosted analytics provider remain outside the project's trust boundary. Browser storage is not an encrypted secrets vault. The complete hosted-statistics boundary, localStorage and IndexedDB inventory, recent-file metadata, clearing behavior, and permission-revocation guidance are in [docs/privacy.md](docs/privacy.md). See [SECURITY.md](SECURITY.md) and [security_posture.md](security_posture.md).

## Browser support

Expand Down Expand Up @@ -66,6 +66,7 @@ npm run validate:source
npm run validate:i18n
npm run validate:accessibility
npm run validate:privacy
npm run validate:pages
npm run validate:artifact
npm run validate:artifact:structure
npm run validate:runtime
Expand All @@ -81,6 +82,8 @@ npm run quality:security:docker

Do not edit generated `index.html` or `dist/` artifacts manually. Change source under `src/`, then rebuild.

The GitHub Pages workflow creates a separate `_site/index.html` with `npm run prepare:pages`. That command requires the public `UMAMI_WEBSITE_ID` variable and injects the reviewed Pages-only analytics loader after all standalone gates pass.

## Architecture

The project is a modular browser monolith:
Expand Down
5 changes: 4 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Product boundary

hSQLite Editor is one browser application distributed as one offline-capable HTML file. SQLite execution, persistence, import/export, and UI behavior run locally in the browser. There is no application backend and no remote data service.
hSQLite Editor is one browser application distributed as one offline-capable HTML file. SQLite execution, persistence, import/export, and UI behavior run locally in the browser. There is no application backend or remote database service. The GitHub Pages deployment is a narrow hosting satellite that adds minimized Umami pageview analytics after the standalone artifact passes validation.

## Source ownership

Expand All @@ -25,6 +25,7 @@ hSQLite Editor is one browser application distributed as one offline-capable HTM
5. `src/app.mjs` is the sole production entry point. Every cross-file dependency is an ESM import/export, and module cycles are blocking failures.
6. User-facing copy and locale formatting are owned by the localization capability.
7. Generated `index.html` and `dist/` files are outputs, not hand-maintained source.
8. Hosted analytics exists only in generated `_site/index.html`, runs only on the exact official Pages origin and path, and never enters tracked standalone or release artifacts.

## Build composition

Expand All @@ -36,6 +37,8 @@ DOM registries and styles are split by owned surface. Numeric filenames define d

The Linux filesystem stage is a packaging satellite over the same versioned HTML artifact. It adds no second runtime, service, persistence layer, file association, or protocol handler. Its launcher resolves the packaged HTML relative to the installed prefix and delegates that one local path to `xdg-open`; [linux-packaging.md](linux-packaging.md) defines the complete boundary.

The GitHub Pages stage is a second deployment satellite over the same validated `index.html`. `scripts/prepare-pages-site.mjs` copies that artifact to `_site`, injects one fixed-origin Umami loader, attaches the package version as a pageview tag, and validates the hosted result. Only the public website ID comes from a GitHub variable. The loader checks the exact Pages origin and project path and honors DNT and GPC before requesting the third-party script. `_site` is generated and never becomes a standalone or release source.

## Architectural change policy

Preserve the modular monolith and standalone distribution unless evidence shows they no longer serve the product. The CodeMirror 6 decision is recorded in `docs/adr/0001-codemirror-6-editor-runtime.md`; the ESM graph and standalone linker decision is recorded in `docs/adr/0002-esm-application-module-graph.md`.
Expand Down
12 changes: 11 additions & 1 deletion docs/privacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@

## Data flow

hSQLite Editor runs in the browser and has no application backend, analytics service, or required telemetry endpoint. The standalone artifact does not intentionally upload database bytes, SQL text, history, favorites, settings, or file metadata. Data leaves the browser only when the user explicitly downloads, exports, copies, or shares it, or when browser/extension/operating-system behavior outside the project trust boundary does so.
hSQLite Editor runs in the browser and has no application backend. The downloaded standalone artifact, `file://` use, local development, and nonofficial hosts do not load an analytics service or intentionally upload database bytes, SQL text, history, favorites, settings, file names, or file metadata. Data leaves those surfaces only when the user explicitly downloads, exports, copies, or shares it, or when browser, extension, or operating-system behavior outside the project trust boundary does so.

Browser storage is local to a browser profile and origin. It is not encrypted application storage and must not be treated as a secrets vault. Storage behavior for `file://` pages varies by browser; use the browser's site-data controls for the artifact origin when removing all data.

## GitHub Pages usage statistics

Only the official deployment at `https://helbertm.github.io/hSQLite-Editor/` conditionally loads the reviewed Umami Cloud tracker. The loader checks the exact origin and project path before making a request. It does not run on `file://`, localhost, local IP addresses, copied HTML, or another domain. It also makes no analytics request when the browser enables Do Not Track or Global Privacy Control.

The hosted deployment records automatic pageviews. Umami can use ordinary pageview and browser metadata to report the page path, referring site, browser, operating system, device category, language, screen size, country, date, and time. URL query parameters and fragments are excluded. The current editor version is attached as a tag such as `hsqlite-editor-v0.7.0`, which allows maintainers to compare aggregate use by version.

The integration does not call custom tracking or identification functions and does not enable custom events, performance collection, session replay, or heatmaps. It does not intentionally send SQLite database contents, database bytes, SQL text, queries, file names, history, favorites, settings, recent-file metadata, exported data, or tab content. Umami Cloud is a third-party hosted service; its network processing, retention, and country derivation are governed by the service configuration and terms rather than by the standalone editor.

The deployment fails closed when the public Umami website ID is absent or malformed. The tracker URL and minimization settings are fixed in reviewed repository code rather than mutable GitHub variables. Analytics being blocked or unavailable does not prevent the editor from starting or working.

## localStorage inventory

The application can store these entries:
Expand Down
6 changes: 4 additions & 2 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@
5. Inspect both readable `index.html` and minified `dist/hSQLite-Editor-v<version>.html` artifacts.
6. Run the browser locale/accessibility matrix on the generated artifact.
7. Run `npm run validate:native:chromium`, then open the final artifact directly with `file://` in current Safari and complete changed assistive-technology spot checks.
8. Confirm no runtime network request is required and no machine-local path is present.
8. Confirm the standalone and release artifacts make no runtime network request and contain no machine-local path or hosted analytics loader.
9. Run `npm run validate:linux`; when publishing a distribution-specific Linux package, complete the host-native installation and desktop-session check defined in [linux-packaging.md](linux-packaging.md).
10. Merge the release PR only after all required GitHub checks pass. Release Please must create the exact tag and a draft release, never a public incomplete release.
11. The publication job rebuilds the created tag, validates the release, creates both attestations, uploads the exact HTML, `sbom.spdx.json`, and `SHA256SUMS` without overwrite semantics, then publishes the draft release last. Any earlier failure must leave the release unpublished as a draft.
12. Verify the GitHub release attachment and Pages deployment match the `hsqlite-editor-v<version>` tag.
12. Verify the GitHub release attachment and Pages deployment match the `hsqlite-editor-v<version>` tag. Confirm the Pages deployment contains exactly one Umami loader tagged with that version, while the release attachment contains none.
13. Run `GH_TOKEN=<read-only-token> npm run validate:github-controls -- --confirm-pages-admin-bypass-disabled` and require exit code `0`.
14. Verify both the provenance and SPDX SBOM attestations cryptographically for the exact HTML artifact.

The exact order and ownership of static, contract, artifact/runtime, browser, security, and host-native checks is defined in [validation.md](validation.md). Do not replace a failed focused layer with a passing broader layer.

`index.html` and `sbom.spdx.json` are tracked, reproducible release evidence. Versioned `dist/` artifacts, `SHA256SUMS`, and the temporary Pages `_site/` tree are generated outputs and must not be committed as release history.

The Pages workflow requires the public repository variable `UMAMI_WEBSITE_ID`. `scripts/prepare-pages-site.mjs` rejects a missing or malformed UUID, fixes the reviewed Umami Cloud script URL in source, and validates `_site/index.html` after injection. Do not place executable HTML, a script URL, or tracking configuration in a mutable GitHub variable.

Generate and verify the portable checksum file with `npm run generate:release-checksums` and `npm run validate:release-assets`. Published checksum entries use release asset basenames, so users can download the HTML, SBOM, and `SHA256SUMS` into one directory and run `shasum -a 256 -c SHA256SUMS`. After publication, verify provenance with `gh attestation verify dist/hSQLite-Editor-v<version>.html --repo <owner>/<repository>`. Verify the associated SPDX predicate by adding `--predicate-type https://spdx.dev/Document/v2.3`.

Immutable release publication is atomic: create the exact tag and draft, build from that tag, validate, attest, upload the complete asset bundle, then publish. The workflow forbids `--clobber`. If upload is partial or an asset-name conflict occurs, delete and recreate the unpublished draft after diagnosing the cause; never repair a release by overwriting an asset.
Expand Down
6 changes: 5 additions & 1 deletion docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ hSQLite Editor separates checks by failure ownership. A passing broader layer ne

## Static policy

Run `npm run validate:static` for source composition, localization ownership, accessibility contracts, privacy documentation, SPDX reproducibility, and workflow syntax. These checks inspect owned files without starting the application.
Run `npm run validate:static` for source composition, localization ownership, accessibility contracts, privacy documentation, GitHub Pages analytics isolation, SPDX reproducibility, and workflow syntax. These checks inspect owned files without starting the application.

`npm run validate:pages` proves that tracked standalone and generated release artifacts contain no tracker, the Pages workflow owns deploy-time preparation, the vendor script URL is fixed in reviewed code, only the website UUID is configurable, the exact official origin and path are enforced, URL query/hash data is excluded, DNT/GPC is honored, and privacy documentation covers the hosted boundary. The Pages preparation command performs a second validation against the generated `_site/index.html`.

## Deterministic unit and contract tests

Expand Down Expand Up @@ -44,6 +46,8 @@ CodeQL analyzes the owned `src/` and `scripts/` trees. Generated standalone arti

This command is intentionally outside `validate:full`, `validate:full:ci`, and normal pull-request CI. Hosted state is networked, permission-sensitive, and non-deterministic. A missing token or manual confirmation is `UNVERIFIED`, never a pass; verified drift and transport failures use different exit codes so operators do not misclassify access failures as product defects.

After a Pages deployment, inspect browser network traffic on the exact public URL. With DNT and GPC disabled, expect one reviewed Umami script and one automatic pageview tagged `hsqlite-editor-v<version>`; verify that query strings, URL fragments, and application content are absent. With DNT or GPC enabled, expect no Umami request. Repeat from localhost and `file://` and expect no Umami request. Vendor-account dashboards and retention settings remain hosted evidence and are not proven by repo-local tests.

## Orchestration

`npm run validate:full` runs static policy, deterministic contracts, readable structure, cross-surface runtime, feature-owned runtime, release structure/runtime, deterministic Linux staging, and approval gates in that order. `npm run validate:full:ci` adds clean-repository checks before and after the same sequence. Browser, security-update, host-native, and hosted GitHub checks remain separate because their environments and failure modes are independent.
Expand Down
Loading