Reading copy of lichen.page.review: one PR per unit, product changes only

ONE-DOOR unit 4: one implementation each for packages, registration, passwords, API errors and paths #70

targeting base/one-door-unit-4 from view/one-door-unit-4
Open opened by and assigned to notplants-bot.bsky.social
targeting base/one-door-unit-4 from view/one-door-unit-4

Packages, site registration, passwords, API errors and package paths each get one implementation.

  • Packages: frontend, plugin and dashboard packages share one cache verifier and bounded snapshot reader, one fetch/identity/proof path, one loader (verification, manifest-policy callback, persistence, in that order), one source and exact-pin validator, one update pipeline, and one complete-source cache key. Source admission runs before any transport and before cached selection. The old readers and unused fetch/copy wrappers are removed.
  • Site registration: create, import and deletion rollback register through one owner-list/index entry that emits the typed events, and create and import share one pre-scaffold reservation check (reserved hosts, index, existing folders and areas, quota, user creation) under the domain lifecycle lock.
  • Passwords: account and share passwords use one Argon2 PHC verifier; the unused file-provider trait is removed.
  • API errors are shaped once, at the boundary: an in-process marker prevents re-shaping, error JSON from the site process is validated before the marker is restored, dashboard and Git handlers return ApiError, internal details are redacted, and storage-full guidance is fixed.
  • Paths: one predicate checks raw relative package paths for the frontend, archive, Git, plugin and dashboard adapters.
  • Plus clean-up: direct pinned-fetch call, merged create-only forwarding layer, and fallbacks that delegate to the shared error conversion.

ONE-DOOR, unit 4 (subunits 4a, 4b, 4c, 4d, 4e, 4 combined, replacing their per-subunit PRs). Plan: https://tangled.org/notplants-bot.bsky.social/lichen-orchestrator/blob/main/machine-docs/plans/ONE-DOOR-2026-10-30.md

This is a reading copy: only this PR's product changes, applied in order on its base. Nothing here is to be merged.

Source PRs on lichen.page.review (product), in order, with their original descriptions #

  • #3532 s/904-od-u4a-cache-product — share bounded pinned cache verification

    Use one cache verifier for frontend, plugin and dashboard packages. Bound Git proof reads for every package kind, retaining archive digest checks and each adapter’s distinct missing, malformed and mismatched proof diagnostics.

  • #3534 s/906-od-u4a-refetch-product — share pinned package refetch verification

    Use one loader for missing frontend and plugin copies. Require the fetched identity to equal the requested pin, verify its files/proof before persistence, and retain each adapter’s fallback and completion behavior.

  • #3536 s/908-od-u4a-fetch-product — share dashboard package fetch verification

    Route dashboard and site package fetch output through the same identity and proof checks. Preserve distinct public fetch and pin refusals, task-scoped dashboard transport fixtures, and manifest validation before persistence.

  • #3538 s/910-od-u4a-admission-product — share pinned source admission

    Use one source and exact-pin validator for dashboard selection and shared package fetch. Refuse invalid identities before polling transport while preserving dashboard refusal text.

  • #3540 s/912-od-u4a-update-product — share package update sequencing

    Serialize frontend and plugin updates through one pipeline for verified fetching, copy persistence, manifest and pin callbacks, and cleanup. Retain adapter-specific transactional source checks and error mapping.

  • #3542 s/914-od-u4a-cached-product — share bounded package cache loading

    Read every cached frontend, plugin and dashboard through the same bounded snapshot and pin verification. Preserve caller refusal and fallback behavior; oversized or linked cached content is refused before verification.

  • #3544 s/916-od-u4a-reader-product — remove unbounded package cache readers

    Delete the old readers after every production cache caller uses bounded snapshots and pin verification.

  • #3546 s/918-od-u4a-key-product — share full-source package cache keys

    Move the dashboard cache-key function unchanged into the shared package module. Preserve existing dashboard cache names; frontend and plugin adapter adoption follows in this unit.

  • #3548 s/920-od-u4a-sourcekey-product — use full-source keys for site package caches

    Use the shared complete-source key for frontend and plugin cache reads, writes, proofs and cleanup. Keep public plugin update handles, manifest names and frontend response names unchanged. Old basename-only caches are no longer selected; missing copies refetch at their existing pins.

  • #3550 s/922-od-u4a-sourcecheck-product — validate sources before package cache and update use

    Apply common source admission before latest-version transport and frontend/plugin cached selection. A valid cached digest cannot bypass source credential or scheme restrictions.

  • #3552 s/924-od-u4a-load-product — share package load validation and persistence

    Make the common loader own verification, manifest-policy callback and persistence in that order for dashboard, frontend and plugin fetches. Dashboard retains its public error mapping and validates before cache writes; site adapters retain background completion behavior.

  • #3555 s/927-od-u4a-forwarders-product — remove unused package forwarding helpers

    Delete unused archive-fetch and Git-copy wrappers, and Settings-based Git fetch wrappers with no production callers. Keep the confined-tree transport primitive.

  • #3557 s/929-od-u4a-write-product — remove test-only package copy wrappers

    Delete Settings-based copy-writing wrappers with no production callers. The shared loader owns live persistence through the confined-tree primitive.

  • #3560 s/932-od-u4b-register-product — centralize site registration and origin events

    Register create, import and deletion rollback through one owner-list/index door. Emit typed created/imported events there, removing API/discovery duplicates. Rollback restores the index before best-effort owner persistence and emits no event; index insertion is idempotent.

  • #3562 s/934-od-u4b-admission-product — share fresh site reservation checks

    Create and import use one pre-scaffold reservation check under the domain lifecycle lock for reserved hosts, index entries, existing folders/areas, owner quota and user creation. Refuse dangling import-folder links before the partial guard can claim them. Retain create typed conflict errors and import folder/area messages; rollback does not pass through this admission.

  • #3564 s/936-od-u4c-password-product — share PHC password verification

    Account and share passwords use one Argon2 PHC verifier while preserving account malformed-hash errors and share refusal. Remove the unused file-provider trait and implementation; retain AuthResult and live credential/login paths. Encoded algorithms, versions, parameters and constant-time verifier remain authoritative.

  • #3567 s/939-od-u4d-shape-product — mark shaped API errors and redact untyped server failures

    ApiError responses carry an in-process marker so repeated boundary layers do not read or reshape them. A JSON content type alone no longer bypasses shaping. Unexpected untyped server errors receive the canonical public message; storage-full guidance remains exempt. Wire-boundary validation and layer consolidation follow in the next part.

  • #3569 s/941-od-u4d-wire-product — validate site error envelopes at the process crossing

    Validate bounded error JSON from the site process before restoring the shaped-error marker. Reject extra fields, malformed codes, oversized bodies and private unexpected server messages; preserve accepted codes, storage guidance and headers. Remove the inner CMS shaper for composed routers while retaining it for standalone CMS construction.

  • #3571 s/943-od-u4d-handlers-product — return typed errors from front server API handlers

    Main dashboard API handlers return the existing ApiError. Shared tuple conversion preserves public client refusal text, redacts unexpected server details and gives 507 the fixed storage recovery guidance. Buffered legacy responses reuse the same conversion. Git-specific handlers and the compatibility admitted-site helper follow separately.

  • #3573 s/945-od-u4d-git-errors-product — use typed errors for dashboard git operations

    Git API handlers and admitted_site return ApiError, using shared conversion for upstream refusals. Internal push and transport details are redacted; the deliberate missing-remote setup instruction remains a typed public message.

  • #3575 s/947-od-u4d-privacy-product — refuse untrusted JSON and forged storage error text

    Unmarked JSON errors are canonicalized rather than copied into a public message. At the process crossing, storage-full envelopes retain only the defined recovery message; forged guidance falls back to that safe message. Typed in-process errors remain unchanged.

  • #3577 s/949-od-u4e-paths-product — share raw relative-path admission across package adapters

    One predicate checks raw relative package paths before normalization. Frontend, archive, Git component, plugin and dashboard adapters delegate to it. Preserve UTF-8 and dotfiles; intentionally refuse empty/dot segments, drives, backslashes and URL metacharacters. Git additionally refuses slash; frontend rejects non-UTF8.

  • #3579 s/951-od-u4-cleanup-product — remove redundant package and registration forwarding helpers

    Load calls the existing pinned-fetch implementation directly; plugin/dashboard path adapters import the common predicate instead of wrapping it. Merge the create-only forwarding layer into guarded creation and reuse its reserved site path, preserving successful PDS-import guard retention.

  • #3581 s/953-od-u4-error-fallback-product — centralize canonical API refusal fallback

    The existing tuple conversion owns canonical messages for empty/untrusted errors. Legacy buffering and invalid wire/JSON fallbacks delegate to it, removing duplicate fallback construction while preserving storage guidance and public client text.

Commits #

  • 1a81e61ac ONE-DOOR unit 4: subunit 4a
  • e58ec4399 ONE-DOOR unit 4: subunit 4b
  • eabf8d312 ONE-DOOR unit 4: subunit 4c
  • 4482ccee8 ONE-DOOR unit 4: subunit 4d
  • a488a71dd ONE-DOOR unit 4: subunit 4e
  • 8da907462 ONE-DOOR unit 4: subunit 4

Count and shas #

count-product-code-lines 5cb6139d005e..8da907462647: NET -10 · counted +609 -619 · moved out of the counted set +0 · comment-only deletions flagged 8 · macros flagged 0 · tests-folder code flagged 0

Base 5cb6139d005e592411f206aaf2dbfd350e8b1dc1 (tip of s/903-od-u3d-import-tests). Head 8da90746264788ecb81d987295eb87eea68ca42a. 23 files changed.

Check #

Every changed file is a file one of the product PRs changed, and each is identical at the head to the same file at the last product branch s/953-od-u4-error-fallback-product.

Files that a tests PR of this unit also changed (so a test-only edit could in principle sit in them): crates/lichen-site/src/api/api_utils.rs, crates/lichen-site/src/pinned.rs.

Resolved/noted: #3534: the diff did not apply on its own; its 3 file(s) were set to what s/906-od-u4a-refetch-product has

Commits 0