feat(couch): production couch channel - discovery + OAuth-minted JWT (PR7+8)#76
Draft
vreshch wants to merge 2 commits into
Draft
feat(couch): production couch channel - discovery + OAuth-minted JWT (PR7+8)#76vreshch wants to merge 2 commits into
vreshch wants to merge 2 commits into
Conversation
|
🎉 PR Validation ✅ PASSED Commit: Checks:
Ready to merge! ✨ |
PR7 of the CouchDB-edge -> git-SoT Obsidian sync. Adds an OPT-IN couch sync channel alongside the existing git sync; off + untouched unless enabled. - src/couch/couch-sync.ts: a thin client (Obsidian requestUrl + Web Crypto, no PouchDB, mobile-safe bundle) replicating the vault's markdown to a per-memory CouchDB with the SAME content-addressed model the server bridge expects (h:<sha256(chunk)> leaves + f:<path> doc). Push on vault events, pull via _changes poll, echo-safe by content compare. - settings: couchEnabled/Endpoint/Db/Authorization + a settings-tab section. - main.ts: starts the controller only when enabled+configured; + a 'CouchDB: sync now' command. Never touches the git sync path. - scripts/couch-local-test.sh + COUCH-SYNC-TESTING.md: a one-command local rig (couch + bridge + git) that prints the plugin settings and LIVE-watches the chain so you can SEE edit-in-Obsidian -> couch doc -> git commit. Verified: full plugin verify green (68 tests, mobile-safe bundle); the plugin's Web Crypto leaf ids match the server's node sha256; and a simulated plugin push (exact encoding) -> couch -> bridge -> a real git commit (sync: notes/hello.md). The Obsidian-app UI itself is the human test step (this is what you load).
Graduate the CouchDB sync from a manual test rig to a discovery-driven
production controller.
- resolve-host: parse the additive couch fields (couch_endpoint,
couch_token_url, couch_vaults[{vault,db}]); git-only payloads keep the
exact shape. channelForVault routes exactly one channel per memory.
- couch-token: CouchTokenClient mints the per-(user,memory) JWT from the
auth service /account/couch-token with the plugin OAuth bearer; caches
until ~60s before expiry, re-mints on demand + on 401.
- couch-sync: replaced the static Authorization header with a token
provider + one-shot 401 re-mint retry; keeps the thin-client doc model
(leaf h:<sha256>, file f:<path>, deterministic revs, new_edits:false).
- main: channel selection in syncNow - couch channel -> couch controller
(one live per memory), else the git controller untouched.
- Removed the manual couch settings + settings-tab UI and the local rig
script; couch is now fully discovery-driven.
- Tests: resolution parsing, channel selection, token cache/refresh/401.
Branch coverage 65.88% -> 79.84% (couch-sync excluded as an
Obsidian-runtime file, same bucket as vault-fs).
e0f69e9 to
7036d50
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Graduates the Obsidian plugin's CouchDB sync from a manual test rig to a discovery-driven production controller. The channel is now selected from the server's
/.well-known/agentage-syncresolution and its JWT is minted from the auth service - no hand-entered endpoint, db, or Authorization header.Discovery contract
The plugin already resolves
/.well-known/agentage-syncper memory. This PR extends parsing to the additive couch fields (server source:agentage/webpackages/sync/src/resolution.ts+hosts.ts):couch_endpoint- the public CouchDB host the device replicates againstcouch_token_url- the auth-service endpoint that mints the couch JWTcouch_vaults: [{ vault, db }]- couch-channel memories with their per-memory dbA memory appears in either
vaults(git) orcouch_vaults(couch), never both - exactly one channel per memory. Git-only payloads (no couch fields, e.g. an older server) parse exactly as before; a partial couch advert (missing endpoint/token url/vaults) degrades to git rather than half-enabling.channelForVault(resolution, vault)returns{channel:'git'}or{channel:'couch', endpoint, db, tokenUrl}.Token flow
CouchTokenClientPOSTs the plugin's existing OAuth bearer to the advertisedcouch_token_url(auth/account/couch-token, body{ memory }). Response envelope{ success, data: { jwt, db, sub, expSec } }. The JWT is cached until ~60s beforeexpSecexpiry, re-minted on demand and on a 401 from CouchDB (one-shot retry). Sync never signs a credential - the auth service is the sole minter.Channel selection
syncNowresolves, then routes: a couch-channel memory drives the couch controller (one liveCouchSyncper memory, rebuilt on a memory switch, live push on md edits + pull interval); every other memory uses the unchanged git controller. The committed thin-client replication logic is kept intact (leaf docsh:<sha256(chunk)>, file docsf:<path>, deterministic1-<rev>,_bulk_docs new_edits:false) - it matches the server bridge - and is now wired to the resolvedendpoint/db+Bearer <couch jwt>.What replaced the rig
couchEnabled/couchEndpoint/couchDb/couchAuthorizationand their settings-tab UI.startCouchSync().scripts/couch-local-test.sh(the local test rig).Couch is now entirely discovery-driven; the normal "Sync now" handles both channels.
Coverage
Branch coverage 65.88% -> 79.84% (global gate is 70%;
npm run test:coveragegreen). New focused tests cover resolution parsing, channel selection, and token cache/refresh/401 (mocked requestUrl-style post).couch-sync.ts(theVault+requestUrl+crypto.subtle-coupled replication driver) is excluded from coverage - same Obsidian-runtime bucket asvault-fs.ts.Constraints kept
requestUrl+ Web Crypto (no PouchDB),isDesktopOnlystays true, TS strict, ESM, named exports.Blocked on the parallel web PR
Server-side OAuth-bearer acceptance on
/account/couch-tokenis landing in a parallelagentage/webPR. TodayrequireUserverifies a session JWT (verifySessionJwt), so a live dev call with the plugin's OAuth bearer will 401 - that is the known server gap, not a plugin bug. This PR is built against the documented contract;CouchTokenClientsurfaces the 401 as "unauthorized (OAuth bearer rejected)".Human validation on dev (once the web PR lands)
AGENTAGE_SITE_FQDN=dev.agentage.io).<memory>: couch synced; edit a note and confirm it lands as a git commit via the bridge (couch -> git).Kept as draft.