Archived
Archive: the extension lives in persimmons/app/extension
This commit is contained in:
1 parent
0e92a80b20
commit
aece02b456
1 file changed
+2
-293
@@ -1,294 +1,3 @@
|
||||
# Current dedicated human-vault taste test
|
||||
# persimmons/extension (archived)
|
||||
|
||||
Version 0.3.6 sends human-account requests only to
|
||||
`https://teahub.xyz/tea/site/chat/persimmons/human-vault/items`. Sign in to
|
||||
shared Teahub, then connect from the product surface at `https://persimmons.app`.
|
||||
List/item reads use empty POST `/read` routes so Chromium sends its genuine
|
||||
extension Origin; the pinned server allowlist is unchanged. **Connect Persimmons** requests only the exact Persimmons host permission. The pinned extension
|
||||
ID remains `kiolgjoohhgdohcafgelhikpgkbmckbg`.
|
||||
|
||||
Human vault keys are namespaced by Persimmons origin and account. Existing Tea
|
||||
ciphertext/local keys are not migrated or overwritten. The separate **Actions
|
||||
tokens / Juice** controls retain their existing explicit Tea permission and
|
||||
session flow. They do not use the new human vault.
|
||||
|
||||
Download the extension ZIP from the Persimmons homepage, extract it, and load
|
||||
`persimmons-extension/` through Chromium Developer mode. Full steps and deployment
|
||||
receipt: [dedicated host](../docs/persimmons-host-receipt.md).
|
||||
|
||||
The historical v0 description below applies to the separate Actions/Juice controls.
|
||||
|
||||
# Persimmons Chromium extension v0
|
||||
|
||||
Standalone MV3; load this `persimmons-extension/` directory using Developer mode →
|
||||
Load unpacked at `chrome://extensions`. No build or dependency install is needed.
|
||||
|
||||
## Use
|
||||
|
||||
1. Sign in to `https://teahub.xyz` in the same browser profile. Visit an HTTPS site
|
||||
such as `https://wp.phly.cloud/wp-admin/` and click the Persimmons toolbar icon.
|
||||
2. Review **Current origin** and **Authentication unknown**. The capture signal is
|
||||
only the current origin. No cookies, page content, passwords or passkeys are read.
|
||||
3. **Connect Teahub / refresh** requests only `https://teahub.xyz/*` permission.
|
||||
Your browser sends its existing Teahub session; no service identity is substituted.
|
||||
4. To **Save authority to My vault**, explicitly paste a provider API token / PAT
|
||||
into the masked field and enter a public source name. The existing create API
|
||||
requires a credential value; origin metadata alone cannot create authority.
|
||||
The field clears on success, failure and popup close. Refresh after ambiguous saves.
|
||||
5. Under **Agent access / Permissions**, review requested scopes (read + draft-write
|
||||
by default), expiry (60 minutes by default), choose a source and enter a public
|
||||
agent/workflow subject. **Squeeze scoped Juice** persists policy metadata.
|
||||
6. **Revoke** changes that Juice policy's status without deleting its source token.
|
||||
**Open Persimmons** opens the existing hosted vault UI.
|
||||
|
||||
Juice v0 is **policy metadata only**. It does not enforce downstream scopes or
|
||||
expiry, perform provider calls, attenuate tokens, or revoke provider sessions.
|
||||
Public names, subjects and notes must contain no secrets. Provider privileges and
|
||||
validity remain unchanged until revoked at the provider.
|
||||
|
||||
## Existing API and storage
|
||||
|
||||
No backend, vault, CORS or MCP changes. Fixed base:
|
||||
`https://teahub.xyz/tea/site/chat/persimmons`.
|
||||
|
||||
| Action | Existing route |
|
||||
| --- | --- |
|
||||
| List sources | GET `/list` (popup projects My vault only) |
|
||||
| Save explicit token | POST `/create` with `vault_kind: "user"` |
|
||||
| List Juice | GET `/juice/list` |
|
||||
| Squeeze | POST `/juice/create` with source, subject, scopes, expiry, My vault |
|
||||
| Revoke Juice | POST `/juice/revoke` with ID and My vault |
|
||||
| Agent read | MCP `persimmons_juice_list(repository?, source_id?)` |
|
||||
|
||||
Requests include browser credentials, reject redirects, bypass cache, have a
|
||||
15-second timeout, and cap encoded bodies at 24 KiB (tokens at 16 KiB).
|
||||
POST uses the existing browser client's JSON-as-text/plain contract. Errors are
|
||||
fixed messages; response bodies are never dumped. Only public metadata is rendered.
|
||||
|
||||
The existing Gitea Actions **secret** stores the token; Actions **variables** store
|
||||
connection and Juice metadata. Those variables are not secret storage. The extension
|
||||
adds no encryption: at-rest protection is delegated to the existing Gitea secret
|
||||
store and its operator configuration, not independently audited here. This is not
|
||||
end-to-end encryption; Teahub receives plaintext over HTTPS and Gitea/authorized
|
||||
Actions retain their existing access. There is no reveal endpoint or new vault.
|
||||
The legacy Actions/Juice module has no storage persistence or content script; the human-vault module and status worker are described below.
|
||||
Tokens exist briefly in JS/network memory; memory erasure is not guaranteed.
|
||||
|
||||
## Threat limits
|
||||
|
||||
Only `activeTab` and optional Teahub host permission are requested. The current tab
|
||||
URL is reduced to an origin; paths, query strings and fragments are not saved.
|
||||
Authentication is deliberately unknown, not inferred from cookies or page text.
|
||||
The extension never scrapes passwords or exports passkey private keys.
|
||||
|
||||
A compromised browser, extension, Teahub host, or authorized Gitea/Actions operator
|
||||
can still misuse a submitted token. Host grants are hostname-based and include all
|
||||
ports. Browser SameSite/origin policy can block session transport; failure stays
|
||||
closed and no cookie is manually injected. Incognito is disabled. Closing the popup
|
||||
can interrupt operations; refresh before retrying. Revoking Juice does not revoke
|
||||
the token, sign out the browser or enforce downstream permissions.
|
||||
|
||||
Chrome documents [activeTab access](https://developer.chrome.com/docs/extensions/develop/concepts/activeTab)
|
||||
and [extension host permissions](https://developer.chrome.com/docs/extensions/develop/concepts/network-requests).
|
||||
|
||||
## Verification
|
||||
|
||||
From repository root:
|
||||
|
||||
```sh
|
||||
node --test persimmons-extension/tests/*.test.mjs
|
||||
XDG_DATA_HOME=/tmp/persimmons-test-data python -m unittest tests.test_persimmons tests.test_persimmons_web tests.test_persimmons_vault_model tests.test_persimmons_juice tests.test_persimmons_juice_http
|
||||
```
|
||||
|
||||
Focused tests exercise permission denial, token save and clearing on failure,
|
||||
origin minimization, safe failures, scoped Juice/revoke and startup refresh.
|
||||
The registered MCP Juice tool is exercised with a generated synthetic sentinel:
|
||||
it never reads a secret route or returns a source credential, including an injected
|
||||
extra credential field in stored Juice metadata. Existing vault isolation/lifecycle
|
||||
checks remain in place. These are not downstream enforcement tests.
|
||||
|
||||
For a signed-in live taste test, use a disposable provider token: follow Use above,
|
||||
reopen to check persistence, call `persimmons_juice_list`, revoke Juice and verify
|
||||
its revoked status. Deny Teahub permission, log out and simulate offline responses;
|
||||
expect safe failures. Never put token values or cookie data in screenshots/logs.
|
||||
|
||||
Next: authenticated disposable-token acceptance; a caller-authorized executor
|
||||
that enforces Juice policy; provider-native OAuth/token rotation and revocation.
|
||||
|
||||
## 0.3.3 taste-test UX
|
||||
|
||||
The packaged orange icons cover Chromium 16/32/48/128 sizes. Keep the same loaded
|
||||
folder/profile and reload after extracting the updated ZIP; the pinned ID is
|
||||
unchanged. Connect → Unlock → **Save an account for this origin** reveals manual
|
||||
username/password entry only on the accepted HTTPS origins. **Encrypt and save**
|
||||
is the sole capture action; typing, navigating, and signing in never capture.
|
||||
Persimmons' own login stays outside the accepted human-vault origin allowlist.
|
||||
Actions/Juice remains available under a collapsed section with its existing
|
||||
separate Teahub permission and auth flow.
|
||||
|
||||
The homepage checks the actual account session, hides redundant sign-in when
|
||||
signed in, and labels its ZIP link **Get / update**. It does not infer installation
|
||||
or unlock state. Account forms request autocomplete off and retain real masked
|
||||
password inputs, validation and clearing; browsers can override that hint and
|
||||
still offer save/autofill. No browser-wide password-manager setting is changed.
|
||||
|
||||
References: [browser autocomplete limits](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Turning_off_form_autocompletion),
|
||||
[Chromium icon packaging](https://developer.chrome.com/docs/extensions/develop/ui/configure-icons).
|
||||
|
||||
## 0.3.4 connection, controlled acceptance and updates
|
||||
|
||||
Connect previously lived only in the popup's `owner` variable; closing the popup
|
||||
lost it. Now `chrome.storage.local` retains only `{owner}` in a dedicated connection
|
||||
hint, in addition to the existing passphrase-wrapped key. Every reopen verifies
|
||||
permission and the live same-owner server session before showing Connected/Locked.
|
||||
A changed account requires explicit Connect; offline/expired sessions fail closed.
|
||||
Passphrases, plaintext credentials and unwrapped keys are never persisted.
|
||||
|
||||
The only new background capability is a read-only external status message from
|
||||
the top-level exact `https://persimmons.app` origin. It compares the page's owner,
|
||||
the saved connection hint and the live server owner, returning only a connected
|
||||
boolean and package version. There is no page command to unlock, fill, reload,
|
||||
read credentials or retrieve keys, and no unlocked signal. The homepage checks on
|
||||
load, focus and Refresh. Old extensions produce an unavailable/update message.
|
||||
The existing exact server extension ID and two-origin human-vault allowlist remain.
|
||||
|
||||
Actions tokens / Juice is now a small collapsed divider, with token authority,
|
||||
scope/expiry and passkey details under separate disclosures. Orange assets and
|
||||
hero/footer source are unchanged.
|
||||
|
||||
### Stable-folder update loop
|
||||
|
||||
Use the homepage or popup **Update extension** link. Save
|
||||
`https://persimmons.app/assets/update-extension.py` once and run:
|
||||
|
||||
```sh
|
||||
python3 update-extension.py "/path/to/already-loaded/persimmons-extension"
|
||||
```
|
||||
|
||||
Close the popup first. The Python 3 helper downloads the dedicated host's HTTPS
|
||||
ZIP, refuses redirects/downgrades, validates bounded archive paths and the pinned
|
||||
manifest key/ID, then replaces files in that same folder (manifest last). It does
|
||||
not access browser storage, run downloaded commands, install a native host or
|
||||
open a local network listener. This uses TLS release trust; the public manifest
|
||||
key is an identity check, not an independent package signature. If interrupted,
|
||||
rerun before Reload. Existing unrelated folder files are retained.
|
||||
|
||||
Then use **Reload** at `chrome://extensions`, or, from 0.3.4 onward, popup **Update
|
||||
extension → Reload installed files**. Retain the same browser profile and do not
|
||||
uninstall or clear storage. The first update from 0.3.3 uses Chrome's Reload.
|
||||
Future rounds reuse the command and folder, without manual download/unzip/select.
|
||||
Python must already be installed (`py` on Windows). Manual in-place ZIP replacement
|
||||
remains a fallback. The helper was exercised on Linux; macOS/Windows are not yet
|
||||
browser-tested.
|
||||
|
||||
An unpacked extension cannot write its installation directory with its extension
|
||||
APIs; `runtime.reload()` rereads installed files and does not download them.
|
||||
`requestUpdateCheck()` is for packaged updates; setting an update URL does not
|
||||
turn a loaded unpacked directory into a Web Store installation. See Chrome's
|
||||
[local development/reload instructions](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world)
|
||||
and [update lifecycle](https://developer.chrome.com/docs/extensions/develop/concepts/extensions-update-lifecycle).
|
||||
|
||||
### Shortest automatic-update distribution path (not published)
|
||||
|
||||
With explicit approval, use a Chrome Web Store developer account, prepare listing
|
||||
and privacy/data-use disclosures plus reviewer instructions, and upload a versioned
|
||||
package. **Private** with named Google-account testers is the narrow taste-test
|
||||
choice; **Unlisted** allows anyone with the link. Both require review. Approved
|
||||
store-installed packages receive Chrome-managed updates, with later versioned ZIPs
|
||||
submitted through the same listing. See [distribution settings](https://developer.chrome.com/docs/webstore/cws-dashboard-distribution)
|
||||
and [updating a listing](https://developer.chrome.com/docs/webstore/update).
|
||||
|
||||
Identity is a pre-publication gate: inspect the assigned store public key/ID and
|
||||
verify it equals `kiolgjoohhgdohcafgelhikpgkbmckbg`. Do not assume that including an
|
||||
arbitrary manifest public key forces the Store to use it. Establish whether the
|
||||
original signing identity can be retained through the supported upload process;
|
||||
no private signing key was sought or exported in this round. If the Store assigns
|
||||
a different ID, stop for an explicitly reviewed identity/key-storage migration;
|
||||
do not widen the server caller allowlist or tell testers to uninstall their
|
||||
existing vault. See [Chrome's key/ID guidance](https://developer.chrome.com/docs/extensions/reference/manifest/key).
|
||||
|
||||
### Retest 0.3.4
|
||||
|
||||
1. Update the same folder and Reload; sign in at persimmons.app.
|
||||
2. Open `https://wp.phly.cloud/wp-login.php`, click 🍊, Connect once and accept the
|
||||
exact Persimmons permission. Initialize/unlock with your vault passphrase.
|
||||
3. Expand Save an account, type a disposable username/password and Encrypt and save.
|
||||
4. Close the popup, refresh the login page, reopen 🍊: Connected/Locked should return
|
||||
automatically, with empty passphrase and Fill disabled. Unlock, select the saved
|
||||
account, Fill current page. Both fields should fill; nothing submits.
|
||||
5. Delete that account. Refresh persimmons.app: Extension connected, with no claim
|
||||
that the vault is unlocked. Sign out: reconnect fails safely; sign back into the
|
||||
same account to restore connection. A different account requires explicit Connect.
|
||||
|
||||
Automated acceptance now uses the real controlled WordPress login form, including
|
||||
actual popup destruction/reopen, both page refreshes, wrong-passphrase rejection,
|
||||
metadata-only storage inventory, explicit fill with zero POST submissions, deletion,
|
||||
and signed-out rejection. It uses disposable values with no provider authority.
|
||||
|
||||
## Next: selective CSV import (not shipped)
|
||||
|
||||
Keep the import hook absent in 0.3.5. A separate change should add **Import CSV**
|
||||
under Settings, enabled only for a live-connected, unlocked vault. Accept one
|
||||
explicitly selected Chrome/Brave password export via a local file input; never
|
||||
read browser password stores or request filesystem/password-manager authority.
|
||||
Parse quoted CSV locally with bounded file/row/field sizes and required
|
||||
`url,username,password` columns. Preview only origin and username, unchecked by
|
||||
default; reject unsupported origins using the existing crypto allowlist, invalid
|
||||
URLs and passkey records. Do not expand the allowlist as part of import.
|
||||
Deduplicate selected records by exact origin and username against decrypted
|
||||
existing records in popup memory; skip duplicates, never overwrite silently.
|
||||
Encrypt each selected record immediately with the existing `encryptItem`, then
|
||||
send only its ciphertext envelope through `vaultRequest`. Stop on lock, owner
|
||||
change or session loss; report per-record success without logging credentials.
|
||||
Clear file contents, preview and plaintext references on completion/lock/close.
|
||||
Show “Delete the exported CSV after importing” at selection and completion.
|
||||
Tests must cover quoted fields, malformed/oversized input, selective import,
|
||||
duplicates, interrupted partial imports and ciphertext-only network requests.
|
||||
Passkeys remain relationship metadata; never import private material.
|
||||
|
||||
## 0.3.6 state hierarchy and auto-lock
|
||||
|
||||
Connect uses the existing Persimmons browser session, never an account password
|
||||
entered into the popup. Sign-in supports browser username/password autofill and
|
||||
returns an already signed-in browser to the homepage. The vault passphrase is a
|
||||
separate, locally chosen encryption secret; account sign-in cannot recover it.
|
||||
|
||||
Before connection, only Connect and the sign-in fallback are shown. Connected but
|
||||
locked shows Unlock and collapsed auto-lock settings. Unlocked supported origins
|
||||
show saved accounts and explicit Save. Actions/Juice stays collapsed and is hidden
|
||||
until unlock on Teahub; its independent auth/server boundary is unchanged. The homepage
|
||||
prominently shows Extension connected, hides Get whenever the extension responds,
|
||||
and offers Update only when its numeric version is older than the built artifact.
|
||||
A failed version check never invents an available update.
|
||||
|
||||
Auto-lock defaults to **On popup close**. Optional **5 / 15 / 60 minutes** are fixed
|
||||
maximum sessions starting at unlock, not sliding inactivity timers. Changing the
|
||||
setting locks immediately. A non-exportable AES CryptoKey lives only in the
|
||||
extension service worker's memory; no plaintext passphrase, vault key, or account
|
||||
is stored in local/session/sync storage. Existing local passphrase-wrapped key
|
||||
records retain their format. No vault key is synced, exported, or sent to a server.
|
||||
|
||||
A bounded 20-second extension API heartbeat keeps the worker available across
|
||||
popup closes; a deadline timer and checks on every operation enforce expiry.
|
||||
Restart, reload, permission removal, failed live owner/session validation, or
|
||||
unexpected worker shutdown fail closed (and can lock earlier). The popup checks
|
||||
status every ten seconds without clearing fields unless the session has ended.
|
||||
Each crypto operation revalidates the live owner. Internal ports accept only this
|
||||
extension's exact popup URL; the website's separate read-only handshake returns
|
||||
installation/version/connection only, never keys, accounts, or unlock state.
|
||||
|
||||
Tradeoff: anyone controlling the same unlocked browser profile can use Fill during
|
||||
the selected window. Choose popup-close locking on shared machines, or Lock vault
|
||||
before leaving. Browser-restart-only unlock is intentionally not offered: all
|
||||
retained sessions have a maximum sixty-minute deadline. There is no recovery when
|
||||
the passphrase or original profile is lost.
|
||||
|
||||
Retest: update the existing folder with the helper at
|
||||
`https://persimmons.app/assets/update-extension.py`, then Reload in
|
||||
`chrome://extensions` (keep the same profile/folder; do not uninstall). Open
|
||||
`https://persimmons.app/` → sign in if needed → 🍊 → Connect. Confirm the homepage
|
||||
says Extension connected / Up to date and has no Get or Update CTA. On
|
||||
`https://wp.phly.cloud/wp-login.php`, unlock → Save for this site → enter disposable credentials → Save.
|
||||
Close/reopen with the default: unlock → select account → Fill (no submit). Choose
|
||||
Settings → Auto-lock settings → After 5 minutes → unlock → close → reopen: still unlocked.
|
||||
Lock explicitly or wait five minutes: Fill is blocked. Unlock → select → Delete.
|
||||
The Persimmons Chromium extension lives in [`persimmons/app`](https://teahub.xyz/persimmons/app) under `extension/`, with its history. The packaged ZIP is published at `https://persimmons.app/assets/extension.zip`. This repository held an older copy and is archived.
|
||||
Reference in new issue
Block a user