Paracci maintains automated test coverage across its core cryptography engine, session lifecycles, and desktop integrations.
Run quick local unit and integration tests from the repository root:
python -m pytest paracci/tests -q
node --test paracci/tests/test_session_clipboard.mjsRun the security and dependency audit suite:
python paracci/audits/guardian.pyRun dependency vulnerability scanning:
python -m pip_audit -r requirements.lock -r requirements-dev.lockRun the Native Verification parity gate before pushing CI/release changes. This is intentionally separate from the quick test loop because it installs locked dependencies, builds frontend assets, runs the full pytest timeout gate, runs Guardian, runs the Node tests, installs Playwright Chromium, and launches the Python-runtime browser-console smoke:
$env:LIBOQS_VERSION = "0.15.0"
$env:LIBOQS_EXPECTED_COMMIT = "97f6b86b1b6d109cfd43cf276ae39c2e776aed80"
$env:OQS_INSTALL_PATH = "<liboqs-install-prefix>"
$env:LIBOQS_LIB_DIR = "<liboqs-install-prefix>\bin"
.venv\Scripts\python.exe tools\ci\native_verify.py --profile windows-local --python .venv\Scripts\python.exeThe local parity profile requires the same pinned liboqs source marker that CI
uses: <liboqs-install-prefix>\.paracci-liboqs-source with version,
expected_commit, and actual_commit values matching the expected immutable
source pin. Missing Python, Node, npm, npx, Playwright, pip-audit, Guardian, or
liboqs prerequisites fail the parity command instead of being silently skipped.
Run the Docker/Linux parity path with the test image when validating Linux behavior from Windows:
docker build -f Dockerfile.test -t paracci-linux-test .
docker run --rm -v "${PWD}:/workspace" -v "/workspace/node_modules" paracci-linux-testGitHub Actions still owns runner-only setup for Native Verification: repository
checkout, pinned Node/Python setup actions, Ubuntu apt packages, and the
.github/actions/install-liboqs composite action that builds and exports the
pinned native liboqs library. The shared runner owns validation orchestration
after that setup is complete.
Run the focused pre-push release validation contract tests:
.venv\Scripts\python.exe -m pytest paracci/tests/test_release_artifact_validation.py paracci/tests/test_release_workflow_security.py paracci/tests/test_build_metadata.py -q
.venv\Scripts\python.exe -m pytest paracci/tests/test_security_docs.py -qRun the focused PNG carrier transport tests:
.venv\Scripts\python.exe -m pytest paracci/tests/test_carrier_core.py paracci/tests/test_carrier_png.py -q
.venv\Scripts\python.exe -m pytest paracci/tests/test_carrier_services.py -q
.venv\Scripts\python.exe -m pytest paracci/tests/test_carrier_routes.py -q
.venv\Scripts\python.exe -m pytest paracci/tests/test_carrier_ui.py paracci/tests/test_security_docs.py -q
node --test paracci/tests/test_carrier_ui.mjsThese suites cover carrier limits and generic errors, PNG round trips and image
budgets, downstream setup/message validation, BurnDB replay behavior, trusted
file-reference and save-grant scopes, loopback route protections, raw-path
rejection, secondary/default-off UI behavior, conservative locale wording, and
the absence of PNG auto-detection in normal .paracci controls.
The local Playwright suite is the deterministic browser-level replacement for
repeatable manual QA of the unified session workspace. It launches the real
run.py --no-gui source runtime with fresh temporary profiles, uses Chromium
with one worker and no retries, binds and navigates only to 127.0.0.1, and
fails external requests or unexpected browser, console, network, and
same-origin HTTP errors.
The current ten-test suite covers:
- Phase 1 bootstrap, service-worker bearer setup, CSRF state, and loopback-only browser policy.
- Phase 2 standard
.paraccifirst-message round trip, attachment presentation, single-open state, and a precisely scoped replay rejection. - Phase 3 PNG carrier message round trip, insufficient-capacity handling, and isolation between standard and carrier drop targets.
- Phase 4A desktop layout stability at
1280x900and the900x700compact desktop window, including no document-level horizontal overflow, reachable session controls, obvious-overlap checks, accessible names, keyboard reachability, tab behavior, and visible focus indicators. - Phase 4B attachment removal through a standard round trip, saving-disabled attachment presentation, and global routing for standard-message and attachment drops.
This is a Windows/Linux desktop application harness. Compact viewport checks are desktop narrow-window regressions for pywebview/browser window layout stability. They do not expand platform coverage beyond supported desktop window sizes. The accessibility checks cover basic names, keyboard reachability, tab behavior, and focus visibility; they are not a full accessibility audit.
Browser E2E proves the source-runtime browser workflow only. It does not prove native save grants, native attachment staging, UIApi or pywebview bridge behavior, operating-system file dialogs, credential-store integration, or packaged executable behavior. Keep those claims in focused Python and native boundary tests and in the packaged-runtime validation described below.
Set the source runtime used by Playwright:
$env:PARACCI_E2E_PYTHON = (Resolve-Path '.\.venv\Scripts\python.exe').PathRun the P0 release-gate E2E set:
npx playwright test paracci/tests/e2e/bootstrap-policy.spec.mjs paracci/tests/e2e/p0-standard.spec.mjs paracci/tests/e2e/p0-carrier.spec.mjs --config playwright.config.mjsRun the full local Playwright E2E suite, including the P1 desktop layout, accessibility, attachment-editing, saving-disabled, and drop-routing coverage:
npx playwright test --config playwright.config.mjsRun the focused Node session-state and carrier-UI tests:
node --test paracci/tests/test_session_state.mjs paracci/tests/test_carrier_ui.mjsRun the focused Python profile-provisioning, carrier route/UI, and documentation guard tests:
.venv\Scripts\python.exe -m pytest paracci/tests/test_e2e_profile_provisioner.py paracci/tests/test_carrier_ui.py paracci/tests/test_carrier_routes.py paracci/tests/test_security_docs.py -qFinish with diff validation:
git diff --check
git diff --cached --checkThe browser suite, Node tests, and Python tests protect different boundaries. Do not substitute one for another: Playwright covers real browser workflows, Node covers session and frontend state contracts, Python covers provisioning, routes, security policy, and native-boundary logic, and focused native bridge/save-grant tests remain required because browser E2E cannot prove those privileged operations.
Playwright traces, screenshots, reports, videos, downloads, and redacted failure
logs belong only under the ignored output/playwright/ tree. Use traces locally
to diagnose failures, then leave them untracked. Do not publish them or attach
them to releases. Treat traces as locally sensitive because they may contain
synthetic decrypted content or ephemeral authentication context.
Never stage Playwright output, generated .paracci files, carrier PNGs,
downloads, traces, screenshots, logs, local paths, secrets, tokens, keys,
passphrases, release artifacts, or files under prototypes/. Before committing,
inspect the staged names and content:
git diff --cached --name-only
git diff --cached --check
git diff --cached --name-only | Select-String 'output[\\/]playwright|prototypes|\.(paracci|png|zip|log)$'
$localPathPattern = '[A-Za-z]:' + '[\\/]|/' + 'home/'
$privateKeyPattern = 'BEGIN .*PRIVATE' + ' KEY'
git diff --cached -- | Select-String "$localPathPattern|$privateKeyPattern"
git status --short --untracked-files=allThe two Select-String scans should produce no matches. Also inspect the staged
diff directly for credentials or sensitive values that do not match those
patterns.
- Update the affected E2E helper and specs in the same change as a session UI behavior or locator change.
- Keep page-driver helpers thin and assertion-free. Assertions belong in specs.
- Prefer accessible roles and labels, followed by existing stable IDs. Add
data-e2eonly when necessary for a dynamic or ambiguous behavior-oriented locator. - Do not add broad console, network, or HTTP allowlists, skips, xfails, or retries to hide regressions.
- Scope every expected negative HTTP response to the exact method, path, status, and test step. All other 4xx/5xx responses, console errors, page errors, request failures, and external requests remain failures.
- Use only synthetic in-memory or temporary files and isolated temporary profiles. Never use caller-provided files or persistent user data.
Run the Python-runtime browser console smoke before release candidates or when frontend/bootstrap/runtime validation changes. This is not part of the quick unit-test loop because it launches Paracci plus a real Chromium browser:
npx playwright install chromium
node tools/ci/browser_console_smoke.mjs --runtime python --python .venv\Scripts\python.exenpx playwright install chromium
node tools/ci/browser_console_smoke.mjs --runtime python --python pythonThe legacy shorthand remains supported:
node tools/ci/browser_console_smoke.mjs --python .venv\Scripts\python.exeThe smoke starts run.py --no-gui on a random loopback port with an isolated
temporary DATA_DIR and browser profile. It opens the authenticated bootstrap
URL printed by the Python runtime, follows the normal service-worker bearer
flow to the Flask-rendered setup/unlock page, and fails on unexpected browser
pageerror, unhandled promise rejection, console.error, same-origin static
asset failures, or app/static 4xx/5xx responses needed for page load. Failure
output is redacted and must not include bearer tokens, CSRF tokens, local paths,
or temporary profile/data directories.
Paracci E2E viewport checks target Windows and Linux desktop use. Compact desktop window cases are desktop narrow-window regressions for pywebview/browser window layout stability: they verify desktop layout stability and no horizontal overflow at supported desktop window sizes. Viewport dimensions do not imply additional platform coverage.
After building a Windows release candidate, run the same browser-console policy against the frozen executable before preparing release assets:
npx playwright install chromium
node tools/ci/browser_console_smoke.mjs --runtime executable --executable builds\windows\Paracci\Paracci.exe --python .venv\Scripts\python.exeAfter prepare-build-assets creates the Windows portable archive, run the
portable ZIP smoke. The command extracts the ZIP into a temporary directory,
validates the expected portable layout, runs the extracted Paracci.exe, and
lets the extracted portable data directory isolate runtime data:
$zip = Get-ChildItem -LiteralPath builds\windows -Filter 'Paracci-Portable-v*.zip' | Select-Object -First 1
node tools/ci/browser_console_smoke.mjs --runtime portable-zip --zip $zip.FullName --python .venv\Scripts\python.exeAfter building a release candidate for the current platform, run the local packaged smoke and artifact checks before pushing a tag:
# Windows after: python build.py --clean --installer
node tools/ci/browser_console_smoke.mjs --runtime executable --executable builds\windows\Paracci\Paracci.exe --python .venv\Scripts\python.exe
.venv\Scripts\python.exe tools/ci/packaged_runtime_smoke.py --platform windows
.venv\Scripts\python.exe tools/ci/release_artifact_validation.py validate-build --platform windows
.venv\Scripts\python.exe tools/ci/release_artifact_validation.py prepare-build-assets --platform windows
$zip = Get-ChildItem -LiteralPath builds\windows -Filter 'Paracci-Portable-v*.zip' | Select-Object -First 1
node tools/ci/browser_console_smoke.mjs --runtime portable-zip --zip $zip.FullName --python .venv\Scripts\python.exe# Linux after: python build.py --clean --appimage --deb
python tools/ci/packaged_runtime_smoke.py --platform linux
python tools/ci/release_artifact_validation.py validate-build --platform linux
python tools/ci/release_artifact_validation.py prepare-build-assets --platform linuxThe Build & Release workflow still owns CI-only release steps: Windows packaged executable and portable ZIP browser-console smoke, Linux AppImage extraction, Debian install/remove checks, AppImage GUI timeout smoke, artifact upload, Sigstore attestation, VirusTotal scanning, and draft GitHub Release creation. The publish workflow remains the only place that verifies the offline manifest signature and publishes the draft.
- Key generation, signature validation, and shared secret derivation (X25519).
- Key derivation and hardening (HKDF-SHA512/HKDF-SHA256 current protocol keys, fixed Argon2id passphrase derivation, and legacy envelope-read compatibility).
- Symmetric envelope encryption and tamper/modification detection (ChaCha20-Poly1305 AEAD).
- Process memory sanitation (wipe buffers and arrays).
- Generating authenticated setup metadata (initiator and responder setup files).
- Handshake verification and out-of-band safety code computation.
- Session bonding and master key derivation.
- SQLite-bound encrypted session state preservation.
- Isolated
dev_setup.pyX/Y profiles, clean unlock-rate state, and the first-message Flask open that bonds Y.
- Sealing and opening
.paraccimessage packages. - Rate limits, step-based evolution ratchets, deliberate out-of-order rejection, and anti-replay counters.
- Legacy v1/v2 message reads and current v3 direct message-key encryption.
- Expiration checks and Time-To-Live (TTL) enforcement.
- Safe assembly/extraction of zipped payload contents (limit verification).
- Single-use message opening checks and SQLite transaction atomic registrations.
- Transition states: Reserved/Opening, Burned, and Failed (retry window recovery).
- Safe file-overwrite and delete functions.
- Local brute-force rates, failed unlock delays, and lockout limits.
- Platform-native credential store bindings (Windows DPAPI, macOS Keychain, Linux Secret Service).
- Verification of two-factor decryption locking behavior per platform.
- Graceful key-binding service failure fallbacks.
- Verification of local Flask server routing and Bearer token check.
- Header validation (Host, Origin, Referer, and Fetch Metadata).
- CSRF validation and cookie flag checks.
- Shared carrier and extracted-payload limits, stable generic errors, and unsupported carrier-kind handling.
- Lossless PNG capacity estimation, exact extracted bytes, metadata stripping, and image decode budgets.
- Existing setup, responder, message, bonding, decrypt, package, and BurnDB validation after extraction.
- Purpose-scoped one-shot trusted references and size-limited managed save grants.
- Protected Flask routes, bounded multipart input, raw-path rejection, no-store behavior, and redacted failures.
- Collapsed secondary controls, explicit PNG selection, safe localized errors, and unchanged normal
.paracciforms and drop zones.
Use temporary test files outside the repository and do not commit generated PNG
or .paracci files.
- Complete the normal initiator/responder setup ceremony and verify the normal
.paraccioutputs remain primary. - Explicitly expand the optional carrier panel, select a sufficiently large lossless PNG cover, and export initiator setup into a PNG carrier.
- Import that setup carrier and verify the normal responder
.paraccifile is the primary result. - Export the responder into a PNG carrier only through the separate secondary action, then import it into the initiator session.
- Seal a message through both the normal
.paracciflow and the explicit PNG carrier flow; open both through their matching controls. - Verify wrong, corrupt, truncated, or modified PNGs fail with localized generic errors and no backend detail.
- Verify a cover with insufficient capacity produces no output and exposes no capacity internals.
- Confirm browser output downloads PNG bytes and Windows native output consumes the existing one-shot save grant.
- Confirm carrier and cover source files remain present and unchanged after import, open, export, and seal.
- Confirm normal
.paraccidrop zones do not auto-detect PNG files and all affected pages render without console errors.
- Focused Carrier Gate: Run all carrier Python and Node commands listed above.
- Normal Flow Regression Gate: Run the full Python suite so setup, responder, message seal/open, package, loopback, broker, and native behavior remain covered outside carrier-specific tests.
- Browser Session Workspace Gate: Run the P0 Playwright release gate, then the full local Playwright suite. Keep the Python-runtime browser-console smoke as the separate bootstrap/render policy check.
- Windows Candidate Gate: After a later version bump and package build, run executable and portable-ZIP browser smoke, packaged runtime smoke, artifact validation, and the manual native save-grant checks above.
- Linux Candidate Gate: Run Docker/Linux parity before release and packaged-runtime validation after building Linux candidates.
- Version Gate: Keep root
VERSIONunchanged during feature work. Perform the version bump only after feature acceptance and release-candidate validation. - Signing Gate: Preserve the offline Ed25519 release-signing model. CI may create a draft, but publication must continue through the signed-manifest verification workflow.
- Artifact Hygiene Gate: Do not commit generated carriers,
.paraccifiles, screenshots, logs, release artifacts, local paths, tokens, keys, passphrases, or secrets. Traces and downloads are also local-only artifacts. - Native WebView Manual Check: Use platform-local debug runs only for pywebview and operating-system behavior that browser E2E cannot prove. Do not repeat the automated desktop layout stability, attachment-editing, saving-disabled, or drop-routing checks as routine manual browser QA.
- Multi-User Simulation: The isolated Playwright profile pair covers the source-runtime X-to-Y first-message route and subsequent receiver send capability. Reserve parallel debug runs (
run.py --user xandrun.py --user y) for native integration investigation rather than the routine browser pre-release gate. - Standalone Binary Packaging Gates: Packaged executables require confirmation on clean target operating systems to verify native shell loading, anti-screenshot behaviors, and proper device key storage registration.