Skip to content

V2/scaffold - #8

Open
marcelgundermann wants to merge 28 commits into
mainfrom
v2/scaffold
Open

marcelgundermann wants to merge 28 commits into
mainfrom
v2/scaffold

Conversation

@marcelgundermann

Copy link
Copy Markdown
Owner

No description provided.

marcelgundermann and others added 10 commits October 29, 2026 13:30
Static-assets-only Worker (encryptee-v1) pointing at a local build of the
v1 branch. See deploy/v1/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v1 lives on the v1 branch (tag v1.0.0) and is served from v1.encryptee.io.
main now holds a pnpm 12 workspace with catalogs, TypeScript 7, Oxlint and
oxfmt, and a one-day minimumReleaseAge supply-chain guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- age v1 header via typage (Encrypter as header builder, Decrypter.decryptHeader)
- parallel STREAM payload: native WebCrypto ChaCha20-Poly1305 when present
  (Chrome 155+), @noble/ciphers otherwise; byte-identical to typage
- coordinator worker + crypto pool wired over MessagePorts by the main thread;
  sliding window with in-order writes and backpressure
- sinks: File System Access, service-worker streaming download, OPFS staging,
  memory and discard
- Job API (subscribe/getSnapshot/cancel/done), wake lock and beforeunload guard
- keys: PQ-by-default generation, age identity files, passphrase-protected
  key files, share links, IndexedDB contacts
- Vitest browser tests: CCTV vectors, typage byte-identity, tamper cases,
  age CLI interop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the engine bench page (pnpm --filter @encryptee/engine bench) and its
first headless results for Chromium and WebKit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- tool page on the engine's Job API (functional skeleton until a design
  prototype is chosen), keys panel with protected key files, share links and
  contacts
- content pages rendered to static HTML at build time, no JavaScript
- vite-plugin-pwa injectManifest service worker: precache, SW download route,
  updates offered only between jobs, /security.json fail-closed check
- strict CSP with Trusted Types (default policy for same-origin script URLs)
- Playwright E2E against wrangler dev: SW download and OPFS round trips,
  offline, wrong passphrase, no CSP violations
- GitHub Actions: lint, format, typecheck, engine tests with age CLI, E2E
- engine: OPFS detection works on the main thread; typage stays in workers
  so the page bundle carries no crypto library

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plan step 6. The tool follows the chosen prototype variant A: one drop
surface, one centered card per step, oversized monochrome type, the mode
inferred from the files' headers.

- drop anywhere on the page; encrypt and decrypt cards; a single giant
  percentage with speed and time left while a job runs; per-file results
- passphrases: a generated 10-word passphrase shown in full with copy, or
  the user's own typed twice; shared by the encrypt and keys screens
- people: saved contacts as toggles, pasted public keys validated in the
  tools worker, share-link keys preselected, PQ/classic mixing warning
- decrypt checks the passphrase or key file against the header before the
  save picker opens, so a wrong one is rejected on the card; if the check
  outlasts the click's activation, one more click opens the picker
- keys: create (post-quantum default), save a protected key file before
  anything else is shown, open an existing key file, manage people
- content pages restyled to match; native radios and <progress> for a11y
- engine: checkCredentials (header-only, in the tools worker) and
  JobSnapshot.updatedAt for throughput

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy dmmulroy/anti-slop@13909c9 into tools/oxlint/anti-slop, register it
as a JS plugin, and enable all 18 generic rules plus the native
oxc/no-accumulating-spread at error. The Effect rules are copied but not
registered. Pin @oxlint/plugins to 1.90.0 to match oxlint, and keep agent
tooling directories and the vendored plugin out of lint and format.

pnpm lint now reports 477 findings in existing source; cleanup follows
separately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run oxlint --fix for the anti-slop require-readable-spacing rule, which
only inserts blank lines between statements (424 lines across 54
files). pnpm lint drops from 477 to 53 findings; the rest need manual
fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ring

Drop the custom prerender Vite plugin. The content pages now mount with
React like the tool page, from one entry that picks the page by
`#root[data-page]`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix the 52 findings left after the spacing autofix; pnpm lint is now
clean without disabling any rule.

- Type assertions: remove 24 by tightening types instead. The payload
  key, cipher and batch paths take Uint8Array<ArrayBuffer>, tools-worker
  messages use named ToolArgs/ToolValue, and pickAndStart takes a
  per-member Omit so startJob needs no cast. The 10 left (missing DOM
  typings, RPC correlation, bench element ids) carry SAFETY comments.
- unknown inputs: serializeError and errorMessage take `cause`;
  ByteSink.abort takes a SerializedError.
- typeof checks and anonymous return types: replaced with truthiness
  checks and named Tuning, PassphraseChoice and Expectation types.

Behaviour changes:
- checkSafety reports `unsafe` when minSafeBuild is not a number
  (`null` used to pass as `ok`).
- errorMessage checks `instanceof EngineError` instead of duck typing.
- The bench drops unknown backend names; webcrypto only when native.
- vitest.config throws on an unknown BROWSERS name and reads exit
  codes from child.exitCode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
encryptee Error Error Oct 30, 2026 4:19pm UTC

Replace the 10 remaining casts, so no SAFETY comment is needed. The
require-safety-comment-for-type-assertion rule stays enabled.

- Missing DOM typings: declare userAgentData, FileSystemHandle.remove()
  and the `raw-secret` importKey overload via `declare global` next to
  their use in the engine, and trustedTypes and launchQueue on Window in
  apps/web/src/env.d.ts.
- Service worker: annotate the untyped ExtendableMessageEvent data
  instead of asserting it.
- Tools worker RPC: each call gets its own MessageChannel, so the reply
  is typed by the tool that was called; the pending map, ids and
  `worker!` go away. The worker switches on the tool name, which narrows
  args, and a tool without a case fails to compile.
- Bench: `$` checks the element with instanceof, and the scenario is
  looked up in a const list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The real wrangler.jsonc files carry the Cloudflare account_id, so they are
git-ignored now. wrangler.example.jsonc and deploy/v1/wrangler.example.jsonc
are the committed templates; CI copies the root one before the E2E run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
marcelgundermann and others added 4 commits October 29, 2026 15:44
SwDownloadSink (the coordinator's end) moves next to createPortStream (the
service worker's end). It only needs MessagePort, so it compiles under both
tsconfigs, and the test now pairs the real producer with the real consumer
over a MessageChannel instead of a hand-written copy of the producer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RunningJob now asks the location for an output per file instead of switching
on kinds in sinkFor and deliver. Each output carries its sink descriptor and
transferables, what to do once the sink is open, how to finish, and what to
release on failure; the SW download and OPFS staging kinds own their state.

- A failure in any of those steps fails the file as save-failed and stops
  the job. A missing service worker no longer hangs it, and run() catches
  everything, so `done` never rejects.
- The encrypt header write now honours cancel; before, a download that
  never granted credit could not be cancelled.
- OPFS staging holds a Web Lock per staged file until its download has had
  time to finish, and sweeps unlocked files before staging. clearStagingArea,
  which nothing called, is gone.
- SaveTarget is now SaveLocation, matching the glossary; FileOutput moves
  with it, and the OPFS sink reports 'saved' like the others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chooseSaveLocation runs straight from the click: it picks the kind for this
browser and file count, shows the save or folder picker if there is one,
and returns a SaveLocation or why not (dismissed, needs-gesture,
unsupported, save-failed). It never throws, and it only picks OPFS staging
once getDirectory() actually opens, which WebKit's private sessions refuse.
whereToSave says beforehand whether Start will ask where to save.

- The cards call chooseSaveLocation and startJob themselves; pickAndStart
  and asksWhereToSave are gone. Both cards now show save-failed.
- chooseSink, detectCapabilities, sizeLimit, SIZE_LIMITS and outputName
  leave the public interface; preflight works out the save path itself.
- The choice is tested through an internal seam with a fake browser:
  kinds, the picker error mapping, the gesture rule, and refused OPFS.
- The bench uses outputName and OPFS_STAGING_DIR instead of copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
marcelgundermann and others added 8 commits October 30, 2026 15:09
Vitest 5 writes them to .vitest/attachments/, which .vitest-attachments/
didn't match, so 26 screenshots from engine test runs were committed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing loaded it, and oxlint's react plugin already runs the same rules,
React Compiler ones included (react/hooks, exhaustive-deps, purity, refs,
immutability, set-state-in-render).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The e2e test listed the pages by hand and skipped the 404 page. PAGES now
lives in pages.ts, which vite.config.ts builds from and the test loops
over, so a new page is checked once it's built. A path that doesn't exist
gets the 404 page through not_found_handling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
apps/web had no tests and ran Vitest with --passWithNoTests. It now runs
them in Node with the app's own Vite config:

- classify(), by the files' headers and never their names
- checkSafety(), against the security.json this repo deploys, too
- errorMessage()
- _headers requires Trusted Types and allows exactly the policies
  trusted-types.ts creates
- wrangler.example.jsonc serves the folder Vite builds into

CI runs them in the check job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pnpm check runs format check, lint, typecheck, Knip and every package's
tests: what to run before calling a change done. CI runs Knip in the
check job.

What Knip found on the first run:
- the engine's direct @vitest/browser, which vitest/browser reaches
  through @vitest/browser-playwright
- exports used only in their own file, now unexported, and SNIFF_SIZE
  and SinkKind, which nothing used
- styles.css and with it tailwindcss, since Knip doesn't follow the
  pages' <link rel=stylesheet>: an entry in knip.jsonc, with why

devEngines makes pnpm refuse a Node older than 24 instead of warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The commands and the environment variables they take, what to run
before calling a change done, the workspace's rules, and the test
conventions. The engine's and the app's write down the rules that lived
only in lint config, package exports and comments: the engine's two
entry points, byte-identity with typage, sw-stream.ts's APIs, the seams
tests use instead of mocks, the CSP and Trusted Types, the pages and
security.json.

Claude Code reads AGENTS.md itself when there's no CLAUDE.md, so there
is none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
marcelgundermann and others added 3 commits October 30, 2026 16:18
vite-plus 1.4.0 replaces vite, vitest, oxlint, oxfmt and @oxlint/plugins.
It bundles the same Vite 8.3.5, Rolldown 1.2.15 and Vitest 5.0.5, so
apps/web/dist builds byte-for-byte as before. Oxlint and Oxfmt are one
minor behind (1.89.0, 0.74.0), with no formatting changes.

- `vp migrate` moved .oxlintrc.json and .oxfmtrc.json into the root
  vite.config.ts and rewrote imports to vite-plus. That includes the
  vendored anti-slop plugin, recorded in its UPSTREAM.md. It also
  aliased the catalog's vite to @voidzero-dev/vite-plus-core.
- Its minimumReleaseAgeExclude list is gone. It exempted vite-plus,
  Oxlint, Oxfmt and Vitest from the one-day release delay.
- Lint, format check, typecheck, Knip, and the app's unit tests and
  build are Vite+ tasks, cached and replayed when nothing they read
  changed. The engine's tests and e2e stay package.json scripts, so
  they always run, since the age CLI, browsers and wrangler are outside
  what the cache can see.
- tsc still typechecks, and lint has no type-aware rules. With planted
  errors, tsgolint caught plain ones in every tsconfig but missed
  noUncheckedIndexedAccess and lib errors under tsconfig.sw, node and
  worker.json. It flagged `!` that tsc requires.
- CI sets up with voidzero-dev/setup-vp, pinned to v1.22.0 by commit.
  Pull requests restore and save the task cache; pushes to main run
  with --no-cache.
- Knip gets the bench's entries, since it doesn't read a `vp dev
  --config` command line. devEngines matches Vite+'s Node range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The _headers CSP and Trusted Types apply only to responses served
straight from static assets, so v2 deploys with no Worker script. That
rules out Void, Cloudflare's full-stack framework, and SSR in general,
and keeps share links fragment-only. A feature needing server state
reopens it.

The research note covers what Vite+ and Void are, their status and
licenses, and what the migration found, with sources.

The glossary's Share link now says the server never sees or stores the
key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Encryptee now keeps no records in browser storage and puts no key in a
URL (ADR 0006). A saved public key can be swapped by anything that runs
in the origin, browser storage gets evicted or cleared, and a link that
preselects a recipient lets its sender choose who can open the files.
To encrypt for someone, the user pastes their public key.

- The engine loses its IndexedDB contacts store and buildShareLink /
  parseShareLink, with their tests.
- The Keys screen is a single card, with "Copy public key" as its only
  copy button. The People card is gone.
- The encrypt card's methods are "Passphrase | Public keys", and
  #to= links no longer preselect a key.
- ADRs 0003, 0004 and 0005 are amended by 0006. The glossary drops
  Contact and Share link.

v2 is unreleased, so there is no stored data or link to migrate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch had an error being deployed

1 failed deployment
Preview — 10e82eb4 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant