Files
app/docs/persimmons-host-receipt.md
T
2026-10-04 02:57:00 +00:00

12 KiB

Persimmons follow-up candidate — 2026-09-30

Version 0.3.4 fixes popup-memory-only connection state with a non-secret owner hint and live session validation on every reopen. Vault keys still die with the popup. A read-only, exact-origin, same-owner handshake lets the signed-in page say Extension connected without exposing unlocked state or secrets. Actions/Juice is a compact collapsed divider with technical details under disclosures. A same-folder Python update helper plus Reload removes repeated manual ZIP handling. Orange icons, hero/footer copy, crypto, server pinning, origin allowlist, dedicated host and MCP boundaries are preserved.

Candidate acceptance: 42 Python tests, 11 Node tests across four files, plus real Chromium 1243 against the live Persimmons API and the real https://wp.phly.cloud/wp-login.php (not an intercepted target fixture). Candidate homepage assets were served locally into the Persimmons origin for pre-publication review. Connect → initialize/unlock → save → destroy popup → refresh both pages → reopen automatically Connected/Locked → wrong-passphrase rejection → unlock/list → fill both fields with zero POST submissions → delete all pass. Homepage handshake, expired/logout session rejection, changed-owner rejection and revoked host permission also pass. No Tea permission was granted. Synthetic accounts remain, but successful tests remove their ciphertext and sign out; no provider credentials or sessions are included in receipts. The normal Chromium permission prompt was not clicked by automation: the harness pregrants the exact permission; the user's accepted prompt is the human evidence.

Package SHA-256: d98ce727ebdedd9ceaec226960103baa58f8a5b65a0b8e31155b5fdea7334996. Initial and pre-publication fetch: HEAD/origin-main both 26fec5fadd444ca2ccfe70c7eeb9ee6688bb7de4, ahead 0 / behind 0; initial clean, pre-commit dirty only with this task. No delegation. Build-generated host vault module remains byte-for-byte copied from the accepted source.

Deployment remains a separate required receipt: dispatch Launch independent Persimmons, .gitea/workflows/persimmons-launch.yml, matcha/teahub, ref main, no inputs, after canonical publication. The local reviewed tea_workflow_dispatch allowlist does not include this workflow; do not extend it through its own running control plane or bypass it with provider secrets. Check the live /healthz source SHA and asset/package bytes after the run, then repeat the browser check without PERSIMMONS_LOCAL_UI=1.

User retest: use Update extension on the homepage, save the helper once, run python3 update-extension.py "/path/to/existing/persimmons-extension", then chrome://extensions → Persimmons → Reload (do not uninstall). Sign in at Persimmons; open the WordPress login URL, Connect once, unlock, save a disposable credential, close/refresh/reopen, unlock, select/Fill without submit, and Delete. Refresh Persimmons to see Extension connected. Future updates reuse the same helper command/folder; 0.3.4 also offers popup Reload installed files.

Remaining: Chrome Web Store private/unlisted distribution needs explicit approval, review and a verified matching store identity. No Store upload/publication was attempted. True self-download of unpacked extension source is unavailable; Reload alone rereads files. Helper browser acceptance is Linux-only. Detailed update mechanics, platform notes and official references are in persimmons-extension/README.md.


Persimmons taste-test receipt — 2026-09-30

Accepted: https://persimmons.app is live. Homepage, health, extension download, exact-origin preflight and real Chromium encrypted-vault flow pass.

Deployment and acceptance

  • Runtime source: 54f0ff75f7b6a89d69dc4a211b44467ffc35be03. Final launch Action 617, job 619, succeeded; complete deployment/check cycle: 52.814 seconds.
  • Before deploy: explicit fetch; worktree HEAD = upstream = runtime source; ahead 0, behind 0, dirty false. Initial canonical source was a80a64f. AGENTS and factory checkpoint read; no delegation.
  • Fly app persimmons-app, iad; one shared CPU / 512 MiB; machine 48e729dc5796e8, release 2, health passing. Encrypted 1 GiB volume vol_rkgqyxm5k2gxm064 persists /data.
  • Image registry.fly.io/persimmons-app:deployment-01M3QY0KQ70X44T1EA7CYETDE0; digest sha256:c99e5711c0dffee68816bd9059e81ce4c9bee6873e6f9b03198d4abae96a234f.
  • Cloudflare apex A 66.241.124.82, AAAA 2a09:8280:1::1a0:f8e1:0; TTL 60, DNS-only. Fly-managed Let's Encrypt RSA/ECDSA certificates are verified and active. No additional hostname or Cloudflare Worker is needed.
  • /, /healthz, app JS, sign-in JS and extension ZIP: 200/nonempty. Health reports the runtime source above. Final extension 0.3.2 matches the tested package byte-for-byte: SHA-256 41dd24474ad35fca6d37378b641c0ded300450bc39145315a2d1d256a87d0430.
  • Preflight on /api/human-vault/items and /api/human-vault/items/read: 204, credentials true, exactly one ACAO: chrome-extension://kiolgjoohhgdohcafgelhikpgkbmckbg. Untrusted origins: 403, no ACAO. No wildcard callers.
  • Live disposable HTTP flow: register/login 200, extension Web Crypto ciphertext create 201, logout/login, persisted read 200, delete 200, logout 200.
  • Real Chromium 1243: homepage and independent signup pass; actual pinned popup has Persimmons permission and no Tea permission; session cookie transport, unlock, encrypted save, explicit fill, lock, unlock/delete and logout rejection all pass. Fill used a locally intercepted HTTPS form; it contacted neither WordPress nor Tea. Successful probes delete their ciphertext and sign out.
  • Ten Node tests and seven Python host/vault/MCP tests pass. Canonical MCP tests assert exactly persimmons_list and persimmons_juice_list, with no human-store access. Connected agent inventory exposes only the metadata tool, no reveal tool.

Scope and preserved boundaries

The host reuses the accepted tea/web/🍊/index.html design, with taste-test controls for independent sign-in and extension download. Repository-token management and Juice remain on the Tea fallback; this round does not migrate those stores or the full repository-management UI. No Tea/StreetPulse runtime dependency was added. No neighboring service was restarted, redeployed, or changed. Tea source/UI and human-vault routes remain available as fallback.

Build copies tea_mcp/human_vault.py byte-for-byte; both copies have SHA-256 8651aed1f8351477cdfd737a9f0d0b38b2e6e7f68edde964980e520a4c9f4f81. Route prefix is /api; empty POST reads append /read. Real Chromium omitted Origin on permitted extension GET requests, which the accepted handler correctly rejected. The POST adapter carries Chromium's genuine Origin into the unchanged read handler; it never invents a caller header or weakens the HTTPS/session/owner checks. The initial HTTP-only launch (614) was corrected before final browser acceptance.

Independent accounts use scrypt password verifiers, throttled same-origin auth POSTs, and revocable 12-hour opaque Secure/HttpOnly host-only sessions. Account passwords are separate from local vault passphrases. No account recovery, local key sync or old-vault migration is added. Wrapped keys are namespaced by origin and account. The separate extension Actions/Juice controls retain their explicit Tea permission/session flow. The new host has no MCP endpoint.

Existing Actions FLY_INFRA / CLOUD_DNS secrets stayed inside the runner. Preflight 610 proved zone reads; 611 created/deleted a unique TXT probe (both 200). No existing DNS records were replaced. Browser shared libraries and fonts were extracted under /tmp; no worker packages were installed.

Exact human taste-test steps

  1. Open https://persimmons.app. Follow Sign in / create account, choose a disposable username and a password of at least 12 characters, and create the account. The homepage should say Signed in to Persimmons.
  2. Download the Chromium extension from the homepage, extract the ZIP, and use chrome://extensions → Developer mode → Load unpacked → extracted persimmons-extension folder. Verify ID kiolgjoohhgdohcafgelhikpgkbmckbg. Update/reload the existing same-ID extension if already installed; retain the same browser profile. Existing Tea keys remain separate.
  3. In that browser profile, open an existing allowed HTTPS login form at https://wp.phly.cloud or https://teahub.xyz. This test does not change either service. Use only the existing disposable-credential test account.
  4. Open the extension. Under My vault · human accounts, click Connect / list this origin and allow persimmons.app. Enter a distinct vault passphrase of at least 12 characters, then Initialize / unlock My vault. The passphrase and decrypted keys stay in the extension.
  5. Enter the disposable account username/password and Encrypt and save account. Close and reopen the popup, reconnect, unlock, select the account, then click Fill. Check the two fields; the extension does not submit the form.
  6. Lock the popup and verify Fill is unavailable. Unlock and delete the encrypted disposable item. Sign out on Persimmons; reconnect must fail safely. Sign in again to verify the same independent account owns the vault.
  7. Agent inspection must expose only connection/Juice metadata through the existing accepted MCP inventory. Never paste passwords, vault passphrases, cookies or ciphertext into chat, logs, screenshots, or issue receipts.

Keep the browser profile and vault passphrase: local keys have no recovery or sync. No existing credential or wrapped-key migration is attempted in this round.

Repeat automated checks

Run python persimmons/build.py, then node --test persimmons-extension/tests/*.test.mjs and python -m unittest tests.test_persimmons_host tests.test_human_vault.

tests/persimmons-host-browser.cjs repeats the live disposable-account browser check from repository root. It requires Playwright and Chromium; optionally set PERSIMMONS_PLAYWRIGHT_MODULE to an installed Playwright module path and PERSIMMONS_CHROMIUM to the executable. Synthetic account verifiers remain; failed early browser probes' unused sessions expire within 12 hours. No real provider credentials were used.

Provider mechanics: Cloudflare DNS API and Fly custom-domain validation.

0.3.5 golf pass — 2026-09-30

Generic popup copy now shows the current site and Connect, then Unlock, then saved accounts / Save for this site. Settings appears only after connection; Juice remains collapsed and appears only when unlocked on its supported Tea origin. Secondary token requests are lazy. Provider hints are absent elsewhere. The homepage removes unused provider/repository controls, hides Get when the extension responds, and shows Update only after a numeric version comparison against the build's version.json. CSV import is not shipped; the extension README specifies the bounded local, selective, deduplicating, ciphertext-only next step. Crypto, origin allowlists, owner/session validation and MCP access are unchanged.

Validation: 38 Persimmons Python tests plus the human-vault checks pass; six Node test files cover crypto, current-origin behavior, no-reveal, connection ownership, popup visibility and homepage version states. Chromium acceptance is inconclusive: local UI navigation and a subsequent browser launch timed out on this worker. No successful real-browser result is claimed for this pass. Initial and pre-publication canonical base: bc1ad9ff8337d18da7ce52704eecbf0f6051d75c, ahead 0 / behind 0, no unrelated worktree edits. No delegation.