# Persimmons Juice v0 Juice derives a Gitea PAT in the unlocked extension from an encrypted Master credential and writes only that derived PAT into a separate named repository vault. The product page sends source ID, target ID/name and selected scopes. Source passwords never pass through the product page, Juice backend or MCP. The extension's existing in-memory key and capture/fill crypto are unchanged. Supported source origins are exactly `https://teahub.xyz` and `https://git.persimmons.app`. Other origins display “Juice adapter not available yet.” Extension host permissions and connect-src enumerate those providers; externally_connectable remains only the Persimmons top frame. The service worker checks the live owner connection, initialization and unlock before decrypting. Provider calls use Basic auth, omit cookies, refuse redirects and have timeouts. The product receives no PAT response. No new MCP surface exists. Choices are `read:repository`, `read:issue`, and `write:issue` (which includes issue reads). These are **provider API-class-wide scopes**, bounded by the source account's authority, not permissions limited to the target repo or a single source repository. No expiry is promised. `all`, admin, user, organization, package and repository-write scopes are not accepted. The modal and every Juice card state this scope boundary. Backend operations are POST `/api/juice/{list,prepare,write,remove,cleanup}`. Only the exact pinned extension Origin can prepare, submit a PAT or confirm provider cleanup; the product page can list safe metadata or remove a target secret. All operations require a live cookie session, matching X-Vault-Owner, and a locally recorded named vault whose immutable repo ID, live owner and privacy are checked against dedicated Gitea. There is no bearer fallback, caller-selected provider host, secret-read operation or raw source input. Actions secrets are named `PERSIM_JUICE_`. Safe operation metadata uses the same name in Actions variables, which Gitea normalizes to uppercase. The backend projects only its strict metadata schema. PATs enter only the write-only secret PUT body, never metadata, sessions, logs, receipts or returns. The dedicated runtime token adds `write:repository` via the existing independent Persimmons launch/bootstrap workflow. Its versioned root-only token file keeps bootstrap resumable; the previous bootstrap token is retained for rollback. Actions workflow execution remains disabled. An operation reserves metadata before creating `persimmons-juice-` on the source provider. This deterministic name permits cleanup even if the create response is lost. Write failure triggers best-effort source revocation and target removal. Unconfirmed cleanup returns ANDON and leaves a visible ledger entry. Worker loss leaves pending metadata for explicit Revoke; it does not persist a plaintext PAT or automatically retry token creation. Rotate first writes a new operation/secret, then removes and revokes the old operation. Interrupted rotation can leave two provider tokens, represented by separate cards; the result explicitly directs Revoke on the old card. Revoke always attempts target removal first. Without an available unlocked source, the target is absent but provider revocation remains unknown. Revoke can be retried after reconnecting/unlocking; revocation tombstones remain visible. Metadata describing a stored token records the last confirmed operation, not continuous provider health monitoring. Inspected before implementation: live Teahub Gitea **1.27.3** OpenAPI, dedicated Gitea **1.27.3** authenticated API, and pinned source: [API router](https://github.com/go-gitea/gitea/blob/v1.27.3/routers/api/v1/api.go), [token create/delete](https://github.com/go-gitea/gitea/blob/v1.27.3/routers/api/v1/user/app.go), and [Gitea API scope documentation](https://docs.gitea.com/development/api-usage/). Validation commands: ``` python persimmons/build.py python -m unittest tests.test_persimmons_host tests.test_persimmons_gitea tests.test_persimmons_ux tests.test_juice_target node --test persimmons-extension/tests/*.test.mjs node tests/juice-provider-integration.mjs node tests/persimmons-host-browser.cjs ``` The provider probe uses a disposable generated non-admin account, checks scoped read/write enforcement, confirms Actions secrets cannot be read, and revokes its PAT. Credentials stay in process memory and never enter output or files. The browser harness retains capture/fill/lock/signout acceptance and exercises Gitea capture, drag, target secret/metadata, rotation and revoke in a fresh profile. Deployment is restricted to `persimmons-launch.yml` after local checks pass. ## Accepted live receipt — 2026-10-01 Implemented in `dfa10102d94f1c962bebc042c80a7de577430897`; final deployed runtime is **`a2ea1c770db8300c0da75db5894e6220ead8ccb4`**. Both releases used only the independent Persimmons launch workflow. [Final launch run 1022](https://teahub.xyz/matcha/teahub/actions/runs/1022), job 1024, completed successfully, including contract tests, provider authority, deployment and HTTP acceptance. Before both publishes/deploys, explicit fetches proved behind=0; dispatch receipts had HEAD=origin/main, ahead=0 and dirty=false. No delegation occurred. Local checks passed: **42 host/Gitea/UX/Juice Python tests**, **8 encrypted-vault and legacy Juice/MCP regression tests**, and **24 extension tests across nine files**. The launch workflow repeated the host/Gitea/UX/Juice and extension suites. The disposable provider integration confirmed Gitea 1.27.3, read:repository access, denied repository writes, write-only secrets, uppercase metadata names, and failed authentication after revocation. Its accepted provider fixture was `juice-probe-93b0fee3e1977e07`, repo ID 10; provider PAT and target test secret were removed. Disposable test users/private repositories remain as test evidence. Final browser acceptance used Chromium **153.0.8010.12**, extension **0.5.0**, and a fresh profile with **no UI asset interception**. User `browser-17b3e5a0b314d214` (non-admin ID 20) created private repo `work-test`, immutable repo ID **19**. The actual Gitea login form was captured into Master, then a native mouse drag emitted dragstart/drop/dragend and opened the modal. The target stayed open after success. Source credential plaintext was neither sent to the product page nor exposed in metadata. The extension worker independently verified two newly issued PATs could read the intended private repository and both returned 401 after their respective revocations. Only boolean counters left that verification closure. The real backend accepted two extension-only PAT writes. Target Actions secret listing exposed names only; secret GET was unavailable. Rotate removed/revoked the old operation and installed a working replacement. Revoke left **zero provider PATs and zero target secrets**. Safe revocation metadata remains in Actions variables. The accepted run removed its Master ciphertext, logged out, and removed its browser profile. The preserved WordPress synthetic capture/fill test passed with zero form submissions; lock/unlock and logout connection clearing also passed. The harness pregrants the exact optional Persimmons host permission and does not claim to automate Chromium's native permission prompt. Timed auto-lock was selected through the existing extension setting to keep the vault unlocked while interacting with the page. Live `/healthz` returned the exact final runtime source above. Live artifact bytes matched the local deterministic build: - app.js SHA-256: `4fc7ab21660660f0d6a3e958c7c54c65bd6d941d12f753a035c7d89d2a074960` - extension.zip SHA-256: `fefa7eeda05543b5216316e8792d32c7358a0f57284ed231490219ef8dd8eb2d` This receipt-only follow-up does not change deployed runtime bytes. Provider scope limitations, lack of automatic expiry, and explicit partial-failure handling remain as documented above.