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; machine48e729dc5796e8, release 2, health passing. Encrypted 1 GiB volumevol_rkgqyxm5k2gxm064persists/data. - Image
registry.fly.io/persimmons-app:deployment-01M3QY0KQ70X44T1EA7CYETDE0; digestsha256:c99e5711c0dffee68816bd9059e81ce4c9bee6873e6f9b03198d4abae96a234f. - Cloudflare apex A
66.241.124.82, AAAA2a09: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-25641dd24474ad35fca6d37378b641c0ded300450bc39145315a2d1d256a87d0430.- Preflight on
/api/human-vault/itemsand/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_listandpersimmons_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
- 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. - Download the Chromium extension from the homepage, extract the ZIP, and use
chrome://extensions→ Developer mode → Load unpacked → extractedpersimmons-extensionfolder. Verify IDkiolgjoohhgdohcafgelhikpgkbmckbg. Update/reload the existing same-ID extension if already installed; retain the same browser profile. Existing Tea keys remain separate. - In that browser profile, open an existing allowed HTTPS login form at
https://wp.phly.cloudorhttps://teahub.xyz. This test does not change either service. Use only the existing disposable-credential test account. - 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. - 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.
- 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.
- 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.