Add ATProto publishing for tip receipts#1
Open
Zakiamangal wants to merge 6 commits intoCooperation-org:mainfrom
Open
Add ATProto publishing for tip receipts#1Zakiamangal wants to merge 6 commits intoCooperation-org:mainfrom
Zakiamangal wants to merge 6 commits intoCooperation-org:mainfrom
Conversation
Full backend rewrite: Node.js/Express → Python/FastAPI with asyncpg. Adds wallet system, pledge fulfillment, Stripe webhooks, receiver payout methods, encryption, and updated frontend pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every completed tip is now published as a signed com.linkedclaims.claim
record on the ATProto network — a verifiable, public receipt.
New file: backend/atproto_publisher.py
- _get_session(): app-password login with cached JWT
- publish_tip(): builds LinkedClaim record and POSTs to ATProto
- publish_batch(): retries unpublished tips (admin endpoint)
Changes to existing files:
- config.py: ATPROTO_HANDLE, ATPROTO_APP_PASSWORD, ATPROTO_SERVICE
settings + atproto_enabled property
- app.py: fire-and-forget publish at 4 tip-completion sites
(wallet tip, manual confirm, Stripe webhook, pledge fulfill)
+ POST /api/admin/atproto/publish-batch endpoint
- requirements.txt: added httpx>=0.27
Key choices:
- httpx for direct XRPC calls (lightweight, async) vs heavy atproto lib
- App-password auth (server-signed) — user OAuth is future work
- Fire-and-forget: publishing never blocks or fails the tip
- Lexicon: com.linkedclaims.claim with claimType "tip"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gvelez17
reviewed
Mar 31, 2026
backend/atproto_publisher.py
Outdated
|
|
||
| record = { | ||
| "$type": "com.linkedclaims.claim", | ||
| "subject": settings.node_url, |
Member
There was a problem hiding this comment.
this is very important here - who is tipping, who is receiving the tip.
What is the meaning of node_url, its always the same. that's not an intereting claim.
@Zakiamangal can you put some thought, what does it make sense to claim, just logically, when a tip is done? what is a tip a claim about, in english?
gvelez17
requested changes
Mar 31, 2026
Member
gvelez17
left a comment
There was a problem hiding this comment.
needs thought about what a tip means
Replace com.linkedclaims.claim with com.thelexfiles.zakia.temp.tip lexicon. Subject is now the tipper (from wallet_contacts), not the app URL. Receiver, amount, contentUrl, and comment are proper fields instead of being buried in a statement string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use com.linkedclaims.claim instead of temp lexicon - subject = tipper DID (who valued the work) - object = receiver name (whose work was valued) - statement = human-readable tip description - source = content URL with howKnown=FIRST_HAND - confidence = 1.0 (tip is a concrete action) - Add batch summary publishing for high-volume periods - Document claim field mapping in module docstring Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add atproto_oauth.py: OAuth 2.0 with PKCE for ATProto - Progressive scope: starts with 'atproto' (read), upgrades to 'transition:authfull' (write) when user wants to publish claims - CRITICAL: never uses transition:generic, only transition:authfull - Add OAuth endpoints: /api/oauth/start, /api/oauth/callback, /api/oauth/upgrade, /api/oauth/session - Add /client-metadata.json for ATProto OAuth client discovery - Add tip-claim.html demo page for publishing tip claims Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct XRPC calls with the @cooperation/claim-atproto SDK. The Python backend calls publish-claim.mjs via subprocess, which uses the SDK's ClaimBuilder for validation and ClaimClient for publishing. This ensures claims conform to the DIF Labs LinkedClaims specification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
com.linkedclaims.claimrecord on the ATProto network — a verifiable, public receiptTaiga: Story #34
Key choices
httpx(direct XRPC calls)atprotoPython lib and TypeScript SDK are overkill for simple createRecord callsasyncio.create_taskfire-and-forgetcom.linkedclaims.claimFiles changed
New
backend/atproto_publisher.py— Core module (~130 lines)_get_session(): login via app password, cache JWT for 90 minpublish_tip(tip_id, conn): build LinkedClaim record, POST to ATProto, store AT-URI in DBpublish_batch(conn, since_hours): retry all unpublished completed tipsModified
backend/config.py— Addedatproto_handle,atproto_app_password,atproto_servicesettings +atproto_enabledpropertybackend/app.py— Added:_publish_tip_safe()wrapper (catches all exceptions, logs errors)POST /api/admin/atproto/publish-batch?since_hours=24admin endpointbackend/requirements.txt— Addedhttpx>=0.27Claim record shape
{ "$type": "com.linkedclaims.claim", "subject": "https://demos.linkedtrust.us/simpletip", "claimType": "tip", "object": "dr-amara-hassan", "statement": "Tip of $5.00 to Dr. Amara Hassan for https://example.com — Great work!", "source": { "uri": "https://example.com", "howKnown": "FIRST_HAND" }, "confidence": 1.0, "effectiveDate": "2026-03-27T17:14:15.319+00:00", "createdAt": "2026-03-27T17:14:15.319+00:00" }What's NOT in scope
Test plan
com.atproto.repo.listRecordsatproto_uriin DB🤖 Generated with Claude Code